diff options
Diffstat (limited to 'drivers/gpu/drm/exynos')
36 files changed, 1732 insertions, 2781 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 43003c4ad80b..bd1a4156f647 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -56,7 +56,7 @@ config DRM_EXYNOS_DSI  config DRM_EXYNOS_DP  	bool "EXYNOS DRM DP driver support" -	depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) +	depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)  	default DRM_EXYNOS  	select DRM_PANEL  	help @@ -77,6 +77,7 @@ config DRM_EXYNOS_VIDI  config DRM_EXYNOS_G2D  	bool "Exynos DRM G2D"  	depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D +	select FRAME_VECTOR  	help  	  Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 7de0b1084fcd..02aecfed6354 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -3,10 +3,9 @@  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.  ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos -exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \ -		exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ -		exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \ -		exynos_drm_plane.o exynos_drm_dmabuf.o +exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fbdev.o \ +		exynos_drm_fb.o exynos_drm_gem.o exynos_drm_core.o \ +		exynos_drm_plane.o  exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o  exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 8b1225f245fc..b3c730770b0f 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -54,6 +54,13 @@ static const char * const decon_clks_name[] = {  	"sclk_decon_eclk",  }; +static const uint32_t decon_formats[] = { +	DRM_FORMAT_XRGB1555, +	DRM_FORMAT_RGB565, +	DRM_FORMAT_XRGB8888, +	DRM_FORMAT_ARGB8888, +}; +  static int decon_enable_vblank(struct exynos_drm_crtc *crtc)  {  	struct decon_context *ctx = crtc->ctx; @@ -152,15 +159,15 @@ static void decon_commit(struct exynos_drm_crtc *crtc)  #define OFFSIZE(x)		(((x) & 0x3fff) << 14)  #define PAGEWIDTH(x)		((x) & 0x3fff) -static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win) +static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, +				 struct drm_framebuffer *fb)  { -	struct exynos_drm_plane *plane = &ctx->planes[win];  	unsigned long val;  	val = readl(ctx->addr + DECON_WINCONx(win));  	val &= ~WINCONx_BPPMODE_MASK; -	switch (plane->pixel_format) { +	switch (fb->pixel_format) {  	case DRM_FORMAT_XRGB1555:  		val |= WINCONx_BPPMODE_16BPP_I1555;  		val |= WINCONx_HAWSWP_F; @@ -186,7 +193,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)  		return;  	} -	DRM_DEBUG_KMS("bpp = %u\n", plane->bpp); +	DRM_DEBUG_KMS("bpp = %u\n", fb->bits_per_pixel);  	/*  	 * In case of exynos, setting dma-burst to 16Word causes permanent @@ -196,7 +203,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)  	 * movement causes unstable DMA which results into iommu crash/tear.  	 */ -	if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { +	if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) {  		val &= ~WINCONx_BURSTLEN_MASK;  		val |= WINCONx_BURSTLEN_8WORD;  	} @@ -219,27 +226,35 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win,  	writel(val, ctx->addr + DECON_SHADOWCON);  } -static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) +static void decon_atomic_begin(struct exynos_drm_crtc *crtc, +					struct exynos_drm_plane *plane)  {  	struct decon_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane; -	u32 val; -	if (win < 0 || win >= WINDOWS_NR) +	if (ctx->suspended)  		return; -	plane = &ctx->planes[win]; +	decon_shadow_protect_win(ctx, plane->zpos, true); +} + +static void decon_update_plane(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane) +{ +	struct decon_context *ctx = crtc->ctx; +	struct drm_plane_state *state = plane->base.state; +	unsigned int win = plane->zpos; +	unsigned int bpp = state->fb->bits_per_pixel >> 3; +	unsigned int pitch = state->fb->pitches[0]; +	u32 val;  	if (ctx->suspended)  		return; -	decon_shadow_protect_win(ctx, win, true); -  	val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y);  	writel(val, ctx->addr + DECON_VIDOSDxA(win)); -	val = COORDINATE_X(plane->crtc_x + plane->crtc_width - 1) | -		COORDINATE_Y(plane->crtc_y + plane->crtc_height - 1); +	val = COORDINATE_X(plane->crtc_x + plane->crtc_w - 1) | +		COORDINATE_Y(plane->crtc_y + plane->crtc_h - 1);  	writel(val, ctx->addr + DECON_VIDOSDxB(win));  	val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | @@ -252,42 +267,33 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win)); -	val = plane->dma_addr[0] + plane->pitch * plane->crtc_height; +	val = plane->dma_addr[0] + pitch * plane->crtc_h;  	writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); -	val = OFFSIZE(plane->pitch - plane->crtc_width * (plane->bpp >> 3)) -		| PAGEWIDTH(plane->crtc_width * (plane->bpp >> 3)); +	val = OFFSIZE(pitch - plane->crtc_w * bpp) +		| PAGEWIDTH(plane->crtc_w * bpp);  	writel(val, ctx->addr + DECON_VIDW0xADD2(win)); -	decon_win_set_pixfmt(ctx, win); +	decon_win_set_pixfmt(ctx, win, state->fb);  	/* window enable */  	val = readl(ctx->addr + DECON_WINCONx(win));  	val |= WINCONx_ENWIN_F;  	writel(val, ctx->addr + DECON_WINCONx(win)); -	decon_shadow_protect_win(ctx, win, false); -  	/* standalone update */  	val = readl(ctx->addr + DECON_UPDATE);  	val |= STANDALONE_UPDATE_F;  	writel(val, ctx->addr + DECON_UPDATE); - -	if (ctx->i80_if) -		atomic_set(&ctx->win_updated, 1);  } -static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win) +static void decon_disable_plane(struct exynos_drm_crtc *crtc, +				struct exynos_drm_plane *plane)  {  	struct decon_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane; +	unsigned int win = plane->zpos;  	u32 val; -	if (win < 0 || win >= WINDOWS_NR) -		return; - -	plane = &ctx->planes[win]; -  	if (ctx->suspended)  		return; @@ -306,6 +312,20 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)  	writel(val, ctx->addr + DECON_UPDATE);  } +static void decon_atomic_flush(struct exynos_drm_crtc *crtc, +				struct exynos_drm_plane *plane) +{ +	struct decon_context *ctx = crtc->ctx; + +	if (ctx->suspended) +		return; + +	decon_shadow_protect_win(ctx, plane->zpos, false); + +	if (ctx->i80_if) +		atomic_set(&ctx->win_updated, 1); +} +  static void decon_swreset(struct decon_context *ctx)  {  	unsigned int tries; @@ -378,7 +398,7 @@ static void decon_disable(struct exynos_drm_crtc *crtc)  	 * a destroyed buffer later.  	 */  	for (i = 0; i < WINDOWS_NR; i++) -		decon_win_disable(crtc, i); +		decon_disable_plane(crtc, &ctx->planes[i]);  	decon_swreset(ctx); @@ -407,7 +427,7 @@ void decon_te_irq_handler(struct exynos_drm_crtc *crtc)  		writel(val, ctx->addr + DECON_TRIGCON);  	} -	drm_handle_vblank(ctx->drm_dev, ctx->pipe); +	drm_crtc_handle_vblank(&ctx->crtc->base);  }  static void decon_clear_channels(struct exynos_drm_crtc *crtc) @@ -460,10 +480,11 @@ static struct exynos_drm_crtc_ops decon_crtc_ops = {  	.enable_vblank		= decon_enable_vblank,  	.disable_vblank		= decon_disable_vblank,  	.commit			= decon_commit, -	.win_commit		= decon_win_commit, -	.win_disable		= decon_win_disable, +	.atomic_begin		= decon_atomic_begin, +	.update_plane		= decon_update_plane, +	.disable_plane		= decon_disable_plane, +	.atomic_flush		= decon_atomic_flush,  	.te_handler		= decon_te_irq_handler, -	.clear_channels		= decon_clear_channels,  };  static int decon_bind(struct device *dev, struct device *master, void *data) @@ -483,7 +504,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)  		type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :  							DRM_PLANE_TYPE_OVERLAY;  		ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], -				1 << ctx->pipe, type, zpos); +				1 << ctx->pipe, type, decon_formats, +				ARRAY_SIZE(decon_formats), zpos);  		if (ret)  			return ret;  	} @@ -497,7 +519,9 @@ static int decon_bind(struct device *dev, struct device *master, void *data)  		goto err;  	} -	ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev); +	decon_clear_channels(ctx->crtc); + +	ret = drm_iommu_attach_device(drm_dev, dev);  	if (ret)  		goto err; @@ -514,8 +538,7 @@ static void decon_unbind(struct device *dev, struct device *master, void *data)  	decon_disable(ctx->crtc);  	/* detach this sub driver from iommu mapping if supported. */ -	if (is_drm_iommu_supported(ctx->drm_dev)) -		drm_iommu_detach_device(ctx->drm_dev, ctx->dev); +	drm_iommu_detach_device(ctx->drm_dev, ctx->dev);  }  static const struct component_ops decon_component_ops = { @@ -533,7 +556,7 @@ static irqreturn_t decon_vsync_irq_handler(int irq, void *dev_id)  	val = readl(ctx->addr + DECON_VIDINTCON1);  	if (val & VIDINTCON1_INTFRMPEND) { -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); +		drm_crtc_handle_vblank(&ctx->crtc->base);  		/* clear */  		writel(VIDINTCON1_INTFRMPEND, ctx->addr + DECON_VIDINTCON1); @@ -547,13 +570,21 @@ static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id)  {  	struct decon_context *ctx = dev_id;  	u32 val; +	int win;  	if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))  		goto out;  	val = readl(ctx->addr + DECON_VIDINTCON1);  	if (val & VIDINTCON1_INTFRMDONEPEND) { -		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); +		for (win = 0 ; win < WINDOWS_NR ; win++) { +			struct exynos_drm_plane *plane = &ctx->planes[win]; + +			if (!plane->pending_fb) +				continue; + +			exynos_drm_crtc_finish_update(ctx->crtc, plane); +		}  		/* clear */  		writel(VIDINTCON1_INTFRMDONEPEND, diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 362532afd1a5..cbdb78ef3bac 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -61,7 +61,7 @@ struct decon_context {  	atomic_t			wait_vsync_event;  	struct exynos_drm_panel_info panel; -	struct exynos_drm_display *display; +	struct drm_encoder *encoder;  };  static const struct of_device_id decon_driver_dt_match[] = { @@ -70,6 +70,18 @@ static const struct of_device_id decon_driver_dt_match[] = {  };  MODULE_DEVICE_TABLE(of, decon_driver_dt_match); +static const uint32_t decon_formats[] = { +	DRM_FORMAT_RGB565, +	DRM_FORMAT_XRGB8888, +	DRM_FORMAT_XBGR8888, +	DRM_FORMAT_RGBX8888, +	DRM_FORMAT_BGRX8888, +	DRM_FORMAT_ARGB8888, +	DRM_FORMAT_ABGR8888, +	DRM_FORMAT_RGBA8888, +	DRM_FORMAT_BGRA8888, +}; +  static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)  {  	struct decon_context *ctx = crtc->ctx; @@ -126,7 +138,9 @@ static int decon_ctx_initialize(struct decon_context *ctx,  	ctx->drm_dev = drm_dev;  	ctx->pipe = priv->pipe++; -	ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, ctx->dev); +	decon_clear_channels(ctx->crtc); + +	ret = drm_iommu_attach_device(drm_dev, ctx->dev);  	if (ret)  		priv->pipe--; @@ -136,8 +150,7 @@ static int decon_ctx_initialize(struct decon_context *ctx,  static void decon_ctx_remove(struct decon_context *ctx)  {  	/* detach this sub driver from iommu mapping if supported. */ -	if (is_drm_iommu_supported(ctx->drm_dev)) -		drm_iommu_detach_device(ctx->drm_dev, ctx->dev); +	drm_iommu_detach_device(ctx->drm_dev, ctx->dev);  }  static u32 decon_calc_clkdiv(struct decon_context *ctx, @@ -271,16 +284,16 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)  	}  } -static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win) +static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, +				 struct drm_framebuffer *fb)  { -	struct exynos_drm_plane *plane = &ctx->planes[win];  	unsigned long val;  	int padding;  	val = readl(ctx->regs + WINCON(win));  	val &= ~WINCONx_BPPMODE_MASK; -	switch (plane->pixel_format) { +	switch (fb->pixel_format) {  	case DRM_FORMAT_RGB565:  		val |= WINCONx_BPPMODE_16BPP_565;  		val |= WINCONx_BURSTLEN_16WORD; @@ -329,7 +342,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)  		break;  	} -	DRM_DEBUG_KMS("bpp = %d\n", plane->bpp); +	DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);  	/*  	 * In case of exynos, setting dma-burst to 16Word causes permanent @@ -339,8 +352,8 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)  	 * movement causes unstable DMA which results into iommu crash/tear.  	 */ -	padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width; -	if (plane->fb_width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) { +	padding = (fb->pitches[0] / (fb->bits_per_pixel >> 3)) - fb->width; +	if (fb->width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {  		val &= ~WINCONx_BURSTLEN_MASK;  		val |= WINCONx_BURSTLEN_8WORD;  	} @@ -382,23 +395,30 @@ static void decon_shadow_protect_win(struct decon_context *ctx,  	writel(val, ctx->regs + SHADOWCON);  } -static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) +static void decon_atomic_begin(struct exynos_drm_crtc *crtc, +					struct exynos_drm_plane *plane)  {  	struct decon_context *ctx = crtc->ctx; -	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; -	struct exynos_drm_plane *plane; -	int padding; -	unsigned long val, alpha; -	unsigned int last_x; -	unsigned int last_y;  	if (ctx->suspended)  		return; -	if (win < 0 || win >= WINDOWS_NR) -		return; +	decon_shadow_protect_win(ctx, plane->zpos, true); +} -	plane = &ctx->planes[win]; +static void decon_update_plane(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane) +{ +	struct decon_context *ctx = crtc->ctx; +	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; +	struct drm_plane_state *state = plane->base.state; +	int padding; +	unsigned long val, alpha; +	unsigned int last_x; +	unsigned int last_y; +	unsigned int win = plane->zpos; +	unsigned int bpp = state->fb->bits_per_pixel >> 3; +	unsigned int pitch = state->fb->pitches[0];  	if (ctx->suspended)  		return; @@ -413,18 +433,15 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	 * is set.  	 */ -	/* protect windows */ -	decon_shadow_protect_win(ctx, win, true); -  	/* buffer start address */  	val = (unsigned long)plane->dma_addr[0];  	writel(val, ctx->regs + VIDW_BUF_START(win)); -	padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width; +	padding = (pitch / bpp) - state->fb->width;  	/* buffer size */ -	writel(plane->fb_width + padding, ctx->regs + VIDW_WHOLE_X(win)); -	writel(plane->fb_height, ctx->regs + VIDW_WHOLE_Y(win)); +	writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win)); +	writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win));  	/* offset from the start of the buffer to read */  	writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win)); @@ -433,25 +450,25 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	DRM_DEBUG_KMS("start addr = 0x%lx\n",  			(unsigned long)val);  	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", -			plane->crtc_width, plane->crtc_height); +			plane->crtc_w, plane->crtc_h);  	/*  	 * OSD position.  	 * In case the window layout goes of LCD layout, DECON fails.  	 */ -	if ((plane->crtc_x + plane->crtc_width) > mode->hdisplay) -		plane->crtc_x = mode->hdisplay - plane->crtc_width; -	if ((plane->crtc_y + plane->crtc_height) > mode->vdisplay) -		plane->crtc_y = mode->vdisplay - plane->crtc_height; +	if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay) +		plane->crtc_x = mode->hdisplay - plane->crtc_w; +	if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay) +		plane->crtc_y = mode->vdisplay - plane->crtc_h;  	val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |  		VIDOSDxA_TOPLEFT_Y(plane->crtc_y);  	writel(val, ctx->regs + VIDOSD_A(win)); -	last_x = plane->crtc_x + plane->crtc_width; +	last_x = plane->crtc_x + plane->crtc_w;  	if (last_x)  		last_x--; -	last_y = plane->crtc_y + plane->crtc_height; +	last_y = plane->crtc_y + plane->crtc_h;  	if (last_y)  		last_y--; @@ -475,7 +492,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	writel(alpha, ctx->regs + VIDOSD_D(win)); -	decon_win_set_pixfmt(ctx, win); +	decon_win_set_pixfmt(ctx, win, state->fb);  	/* hardware window 0 doesn't support color key. */  	if (win != 0) @@ -495,17 +512,13 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	writel(val, ctx->regs + DECON_UPDATE);  } -static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win) +static void decon_disable_plane(struct exynos_drm_crtc *crtc, +				struct exynos_drm_plane *plane)  {  	struct decon_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane; +	unsigned int win = plane->zpos;  	u32 val; -	if (win < 0 || win >= WINDOWS_NR) -		return; - -	plane = &ctx->planes[win]; -  	if (ctx->suspended)  		return; @@ -517,14 +530,22 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)  	val &= ~WINCONx_ENWIN;  	writel(val, ctx->regs + WINCON(win)); -	/* unprotect windows */ -	decon_shadow_protect_win(ctx, win, false); -  	val = readl(ctx->regs + DECON_UPDATE);  	val |= DECON_UPDATE_STANDALONE_F;  	writel(val, ctx->regs + DECON_UPDATE);  } +static void decon_atomic_flush(struct exynos_drm_crtc *crtc, +					struct exynos_drm_plane *plane) +{ +	struct decon_context *ctx = crtc->ctx; + +	if (ctx->suspended) +		return; + +	decon_shadow_protect_win(ctx, plane->zpos, false); +} +  static void decon_init(struct decon_context *ctx)  {  	u32 val; @@ -601,7 +622,7 @@ static void decon_disable(struct exynos_drm_crtc *crtc)  	 * a destroyed buffer later.  	 */  	for (i = 0; i < WINDOWS_NR; i++) -		decon_win_disable(crtc, i); +		decon_disable_plane(crtc, &ctx->planes[i]);  	clk_disable_unprepare(ctx->vclk);  	clk_disable_unprepare(ctx->eclk); @@ -621,9 +642,10 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {  	.enable_vblank = decon_enable_vblank,  	.disable_vblank = decon_disable_vblank,  	.wait_for_vblank = decon_wait_for_vblank, -	.win_commit = decon_win_commit, -	.win_disable = decon_win_disable, -	.clear_channels = decon_clear_channels, +	.atomic_begin = decon_atomic_begin, +	.update_plane = decon_update_plane, +	.disable_plane = decon_disable_plane, +	.atomic_flush = decon_atomic_flush,  }; @@ -631,6 +653,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)  {  	struct decon_context *ctx = (struct decon_context *)dev_id;  	u32 val, clear_bit; +	int win;  	val = readl(ctx->regs + VIDINTCON1); @@ -643,8 +666,15 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)  		goto out;  	if (!ctx->i80_if) { -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); -		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); +		drm_crtc_handle_vblank(&ctx->crtc->base); +		for (win = 0 ; win < WINDOWS_NR ; win++) { +			struct exynos_drm_plane *plane = &ctx->planes[win]; + +			if (!plane->pending_fb) +				continue; + +			exynos_drm_crtc_finish_update(ctx->crtc, plane); +		}  		/* set wait vsync event to zero and wake up queue. */  		if (atomic_read(&ctx->wait_vsync_event)) { @@ -675,7 +705,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)  		type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :  						DRM_PLANE_TYPE_OVERLAY;  		ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], -					1 << ctx->pipe, type, zpos); +					1 << ctx->pipe, type, decon_formats, +					ARRAY_SIZE(decon_formats), zpos);  		if (ret)  			return ret;  	} @@ -689,8 +720,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)  		return PTR_ERR(ctx->crtc);  	} -	if (ctx->display) -		exynos_drm_create_enc_conn(drm_dev, ctx->display); +	if (ctx->encoder) +		exynos_dpi_bind(drm_dev, ctx->encoder);  	return 0; @@ -703,8 +734,8 @@ static void decon_unbind(struct device *dev, struct device *master,  	decon_disable(ctx->crtc); -	if (ctx->display) -		exynos_dpi_remove(ctx->display); +	if (ctx->encoder) +		exynos_dpi_remove(ctx->encoder);  	decon_ctx_remove(ctx);  } @@ -789,9 +820,9 @@ static int decon_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, ctx); -	ctx->display = exynos_dpi_probe(dev); -	if (IS_ERR(ctx->display)) { -		ret = PTR_ERR(ctx->display); +	ctx->encoder = exynos_dpi_probe(dev); +	if (IS_ERR(ctx->encoder)) { +		ret = PTR_ERR(ctx->encoder);  		goto err_iounmap;  	} diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 172b8002a2c8..d66ade0efac8 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -32,19 +32,20 @@  #include <drm/drm_panel.h>  #include "exynos_dp_core.h" +#include "exynos_drm_crtc.h"  #define ctx_from_connector(c)	container_of(c, struct exynos_dp_device, \  					connector)  static inline struct exynos_drm_crtc *dp_to_crtc(struct exynos_dp_device *dp)  { -	return to_exynos_crtc(dp->encoder->crtc); +	return to_exynos_crtc(dp->encoder.crtc);  } -static inline struct exynos_dp_device * -display_to_dp(struct exynos_drm_display *d) +static inline struct exynos_dp_device *encoder_to_dp( +						struct drm_encoder *e)  { -	return container_of(d, struct exynos_dp_device, display); +	return container_of(e, struct exynos_dp_device, encoder);  }  struct bridge_init { @@ -795,9 +796,6 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)  	/* Configure video slave mode */  	exynos_dp_enable_video_master(dp, 0); -	/* Enable video */ -	exynos_dp_start_video(dp); -  	timeout_loop = 0;  	for (;;) { @@ -891,9 +889,9 @@ static void exynos_dp_hotplug(struct work_struct *work)  		drm_helper_hpd_irq_event(dp->drm_dev);  } -static void exynos_dp_commit(struct exynos_drm_display *display) +static void exynos_dp_commit(struct drm_encoder *encoder)  { -	struct exynos_dp_device *dp = display_to_dp(display); +	struct exynos_dp_device *dp = encoder_to_dp(encoder);  	int ret;  	/* Keep the panel disabled while we configure video */ @@ -938,6 +936,9 @@ static void exynos_dp_commit(struct exynos_drm_display *display)  		if (drm_panel_enable(dp->panel))  			DRM_ERROR("failed to enable the panel\n");  	} + +	/* Enable video */ +	exynos_dp_start_video(dp);  }  static enum drm_connector_status exynos_dp_detect( @@ -994,7 +995,7 @@ static struct drm_encoder *exynos_dp_best_encoder(  {  	struct exynos_dp_device *dp = ctx_from_connector(connector); -	return dp->encoder; +	return &dp->encoder;  }  static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { @@ -1019,15 +1020,12 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,  	return 0;  } -static int exynos_dp_create_connector(struct exynos_drm_display *display, -				struct drm_encoder *encoder) +static int exynos_dp_create_connector(struct drm_encoder *encoder)  { -	struct exynos_dp_device *dp = display_to_dp(display); +	struct exynos_dp_device *dp = encoder_to_dp(encoder);  	struct drm_connector *connector = &dp->connector;  	int ret; -	dp->encoder = encoder; -  	/* Pre-empt DP connector creation if there's a bridge */  	if (dp->bridge) {  		ret = exynos_drm_attach_lcd_bridge(dp, encoder); @@ -1054,20 +1052,22 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,  	return ret;  } -static void exynos_dp_phy_init(struct exynos_dp_device *dp) +static bool exynos_dp_mode_fixup(struct drm_encoder *encoder, +				 const struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode)  { -	if (dp->phy) -		phy_power_on(dp->phy); +	return true;  } -static void exynos_dp_phy_exit(struct exynos_dp_device *dp) +static void exynos_dp_mode_set(struct drm_encoder *encoder, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode)  { -	if (dp->phy) -		phy_power_off(dp->phy);  } -static void exynos_dp_poweron(struct exynos_dp_device *dp) +static void exynos_dp_enable(struct drm_encoder *encoder)  { +	struct exynos_dp_device *dp = encoder_to_dp(encoder);  	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);  	if (dp->dpms_mode == DRM_MODE_DPMS_ON) @@ -1084,14 +1084,17 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)  		crtc->ops->clock_enable(dp_to_crtc(dp), true);  	clk_prepare_enable(dp->clock); -	exynos_dp_phy_init(dp); +	phy_power_on(dp->phy);  	exynos_dp_init_dp(dp);  	enable_irq(dp->irq); -	exynos_dp_commit(&dp->display); +	exynos_dp_commit(&dp->encoder); + +	dp->dpms_mode = DRM_MODE_DPMS_ON;  } -static void exynos_dp_poweroff(struct exynos_dp_device *dp) +static void exynos_dp_disable(struct drm_encoder *encoder)  { +	struct exynos_dp_device *dp = encoder_to_dp(encoder);  	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);  	if (dp->dpms_mode != DRM_MODE_DPMS_ON) @@ -1106,7 +1109,7 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)  	disable_irq(dp->irq);  	flush_work(&dp->hotplug_work); -	exynos_dp_phy_exit(dp); +	phy_power_off(dp->phy);  	clk_disable_unprepare(dp->clock);  	if (crtc->ops->clock_enable) @@ -1116,31 +1119,19 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)  		if (drm_panel_unprepare(dp->panel))  			DRM_ERROR("failed to turnoff the panel\n");  	} -} - -static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) -{ -	struct exynos_dp_device *dp = display_to_dp(display); -	switch (mode) { -	case DRM_MODE_DPMS_ON: -		exynos_dp_poweron(dp); -		break; -	case DRM_MODE_DPMS_STANDBY: -	case DRM_MODE_DPMS_SUSPEND: -	case DRM_MODE_DPMS_OFF: -		exynos_dp_poweroff(dp); -		break; -	default: -		break; -	} -	dp->dpms_mode = mode; +	dp->dpms_mode = DRM_MODE_DPMS_OFF;  } -static struct exynos_drm_display_ops exynos_dp_display_ops = { -	.create_connector = exynos_dp_create_connector, -	.dpms = exynos_dp_dpms, -	.commit = exynos_dp_commit, +static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = { +	.mode_fixup = exynos_dp_mode_fixup, +	.mode_set = exynos_dp_mode_set, +	.enable = exynos_dp_enable, +	.disable = exynos_dp_disable, +}; + +static struct drm_encoder_funcs exynos_dp_encoder_funcs = { +	.destroy = drm_encoder_cleanup,  };  static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev) @@ -1219,9 +1210,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)  	struct exynos_dp_device *dp = dev_get_drvdata(dev);  	struct platform_device *pdev = to_platform_device(dev);  	struct drm_device *drm_dev = data; +	struct drm_encoder *encoder = &dp->encoder;  	struct resource *res;  	unsigned int irq_flags; -	int ret = 0; +	int pipe, ret = 0;  	dp->dev = &pdev->dev;  	dp->dpms_mode = DRM_MODE_DPMS_OFF; @@ -1297,7 +1289,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)  	INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug); -	exynos_dp_phy_init(dp); +	phy_power_on(dp->phy);  	exynos_dp_init_dp(dp); @@ -1311,7 +1303,28 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)  	dp->drm_dev = drm_dev; -	return exynos_drm_create_enc_conn(drm_dev, &dp->display); +	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev, +						  EXYNOS_DISPLAY_TYPE_LCD); +	if (pipe < 0) +		return pipe; + +	encoder->possible_crtcs = 1 << pipe; + +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + +	drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs); + +	ret = exynos_dp_create_connector(encoder); +	if (ret) { +		DRM_ERROR("failed to create connector ret = %d\n", ret); +		drm_encoder_cleanup(encoder); +		return ret; +	} + +	return 0;  }  static void exynos_dp_unbind(struct device *dev, struct device *master, @@ -1319,7 +1332,7 @@ static void exynos_dp_unbind(struct device *dev, struct device *master,  {  	struct exynos_dp_device *dp = dev_get_drvdata(dev); -	exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF); +	exynos_dp_disable(&dp->encoder);  }  static const struct component_ops exynos_dp_ops = { @@ -1338,8 +1351,6 @@ static int exynos_dp_probe(struct platform_device *pdev)  	if (!dp)  		return -ENOMEM; -	dp->display.type = EXYNOS_DISPLAY_TYPE_LCD; -	dp->display.ops = &exynos_dp_display_ops;  	platform_set_drvdata(pdev, dp);  	panel_node = of_parse_phandle(dev->of_node, "panel", 0); @@ -1377,7 +1388,7 @@ static int exynos_dp_suspend(struct device *dev)  {  	struct exynos_dp_device *dp = dev_get_drvdata(dev); -	exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF); +	exynos_dp_disable(&dp->encoder);  	return 0;  } @@ -1385,7 +1396,7 @@ static int exynos_dp_resume(struct device *dev)  {  	struct exynos_dp_device *dp = dev_get_drvdata(dev); -	exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_ON); +	exynos_dp_enable(&dp->encoder);  	return 0;  }  #endif diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index a4e799679669..e413b6f7b0e7 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -147,11 +147,10 @@ struct link_train {  };  struct exynos_dp_device { -	struct exynos_drm_display display; +	struct drm_encoder	encoder;  	struct device		*dev;  	struct drm_device	*drm_dev;  	struct drm_connector	connector; -	struct drm_encoder	*encoder;  	struct drm_panel	*panel;  	struct drm_bridge	*bridge;  	struct clk		*clock; diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c deleted file mode 100644 index 24994ba10e28..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ /dev/null @@ -1,186 +0,0 @@ -/* exynos_drm_buf.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/exynos_drm.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" -#include "exynos_drm_buf.h" -#include "exynos_drm_iommu.h" - -static int lowlevel_buffer_allocate(struct drm_device *dev, -		unsigned int flags, struct exynos_drm_gem_buf *buf) -{ -	int ret = 0; -	enum dma_attr attr; -	unsigned int nr_pages; - -	if (buf->dma_addr) { -		DRM_DEBUG_KMS("already allocated.\n"); -		return 0; -	} - -	init_dma_attrs(&buf->dma_attrs); - -	/* -	 * if EXYNOS_BO_CONTIG, fully physically contiguous memory -	 * region will be allocated else physically contiguous -	 * as possible. -	 */ -	if (!(flags & EXYNOS_BO_NONCONTIG)) -		dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs); - -	/* -	 * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping -	 * else cachable mapping. -	 */ -	if (flags & EXYNOS_BO_WC || !(flags & EXYNOS_BO_CACHABLE)) -		attr = DMA_ATTR_WRITE_COMBINE; -	else -		attr = DMA_ATTR_NON_CONSISTENT; - -	dma_set_attr(attr, &buf->dma_attrs); -	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs); - -	nr_pages = buf->size >> PAGE_SHIFT; - -	if (!is_drm_iommu_supported(dev)) { -		dma_addr_t start_addr; -		unsigned int i = 0; - -		buf->pages = drm_calloc_large(nr_pages, sizeof(struct page *)); -		if (!buf->pages) { -			DRM_ERROR("failed to allocate pages.\n"); -			return -ENOMEM; -		} - -		buf->cookie = dma_alloc_attrs(dev->dev, -					buf->size, -					&buf->dma_addr, GFP_KERNEL, -					&buf->dma_attrs); -		if (!buf->cookie) { -			DRM_ERROR("failed to allocate buffer.\n"); -			ret = -ENOMEM; -			goto err_free; -		} - -		start_addr = buf->dma_addr; -		while (i < nr_pages) { -			buf->pages[i] = phys_to_page(start_addr); -			start_addr += PAGE_SIZE; -			i++; -		} -	} else { - -		buf->pages = dma_alloc_attrs(dev->dev, buf->size, -					&buf->dma_addr, GFP_KERNEL, -					&buf->dma_attrs); -		if (!buf->pages) { -			DRM_ERROR("failed to allocate buffer.\n"); -			return -ENOMEM; -		} -	} - -	buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages); -	if (IS_ERR(buf->sgt)) { -		DRM_ERROR("failed to get sg table.\n"); -		ret = PTR_ERR(buf->sgt); -		goto err_free_attrs; -	} - -	DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", -			(unsigned long)buf->dma_addr, -			buf->size); - -	return ret; - -err_free_attrs: -	dma_free_attrs(dev->dev, buf->size, buf->pages, -			(dma_addr_t)buf->dma_addr, &buf->dma_attrs); -	buf->dma_addr = (dma_addr_t)NULL; -err_free: -	if (!is_drm_iommu_supported(dev)) -		drm_free_large(buf->pages); - -	return ret; -} - -static void lowlevel_buffer_deallocate(struct drm_device *dev, -		unsigned int flags, struct exynos_drm_gem_buf *buf) -{ -	if (!buf->dma_addr) { -		DRM_DEBUG_KMS("dma_addr is invalid.\n"); -		return; -	} - -	DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", -			(unsigned long)buf->dma_addr, -			buf->size); - -	sg_free_table(buf->sgt); - -	kfree(buf->sgt); -	buf->sgt = NULL; - -	if (!is_drm_iommu_supported(dev)) { -		dma_free_attrs(dev->dev, buf->size, buf->cookie, -				(dma_addr_t)buf->dma_addr, &buf->dma_attrs); -		drm_free_large(buf->pages); -	} else -		dma_free_attrs(dev->dev, buf->size, buf->pages, -				(dma_addr_t)buf->dma_addr, &buf->dma_attrs); - -	buf->dma_addr = (dma_addr_t)NULL; -} - -struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, -						unsigned int size) -{ -	struct exynos_drm_gem_buf *buffer; - -	DRM_DEBUG_KMS("desired size = 0x%x\n", size); - -	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); -	if (!buffer) -		return NULL; - -	buffer->size = size; -	return buffer; -} - -void exynos_drm_fini_buf(struct drm_device *dev, -				struct exynos_drm_gem_buf *buffer) -{ -	kfree(buffer); -	buffer = NULL; -} - -int exynos_drm_alloc_buf(struct drm_device *dev, -		struct exynos_drm_gem_buf *buf, unsigned int flags) -{ - -	/* -	 * allocate memory region and set the memory information -	 * to vaddr and dma_addr of a buffer object. -	 */ -	if (lowlevel_buffer_allocate(dev, flags, buf) < 0) -		return -ENOMEM; - -	return 0; -} - -void exynos_drm_free_buf(struct drm_device *dev, -		unsigned int flags, struct exynos_drm_gem_buf *buffer) -{ - -	lowlevel_buffer_deallocate(dev, flags, buffer); -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h deleted file mode 100644 index a6412f19673c..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* exynos_drm_buf.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Author: Inki Dae <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#ifndef _EXYNOS_DRM_BUF_H_ -#define _EXYNOS_DRM_BUF_H_ - -/* create and initialize buffer object. */ -struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, -						unsigned int size); - -/* destroy buffer object. */ -void exynos_drm_fini_buf(struct drm_device *dev, -				struct exynos_drm_gem_buf *buffer); - -/* allocate physical memory region and setup sgt. */ -int exynos_drm_alloc_buf(struct drm_device *dev, -				struct exynos_drm_gem_buf *buf, -				unsigned int flags); - -/* release physical memory region, and sgt. */ -void exynos_drm_free_buf(struct drm_device *dev, -				unsigned int flags, -				struct exynos_drm_gem_buf *buffer); - -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 4c9f972eaa07..c68a6a2a9b57 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -15,46 +15,10 @@  #include <drm/drmP.h>  #include "exynos_drm_drv.h"  #include "exynos_drm_crtc.h" -#include "exynos_drm_encoder.h"  #include "exynos_drm_fbdev.h"  static LIST_HEAD(exynos_drm_subdrv_list); -int exynos_drm_create_enc_conn(struct drm_device *dev, -					struct exynos_drm_display *display) -{ -	struct drm_encoder *encoder; -	int ret; -	unsigned long possible_crtcs = 0; - -	ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type); -	if (ret < 0) -		return ret; - -	possible_crtcs |= 1 << ret; - -	/* create and initialize a encoder for this sub driver. */ -	encoder = exynos_drm_encoder_create(dev, display, possible_crtcs); -	if (!encoder) { -		DRM_ERROR("failed to create encoder\n"); -		return -EFAULT; -	} - -	display->encoder = encoder; - -	ret = display->ops->create_connector(display, encoder); -	if (ret) { -		DRM_ERROR("failed to create connector ret = %d\n", ret); -		goto err_destroy_encoder; -	} - -	return 0; - -err_destroy_encoder: -	encoder->funcs->destroy(encoder); -	return ret; -} -  int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)  {  	if (!subdrv) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 644b4b76e071..0872aa2f450f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -19,21 +19,15 @@  #include "exynos_drm_crtc.h"  #include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h"  #include "exynos_drm_plane.h"  static void exynos_drm_crtc_enable(struct drm_crtc *crtc)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); -	if (exynos_crtc->enabled) -		return; -  	if (exynos_crtc->ops->enable)  		exynos_crtc->ops->enable(exynos_crtc); -	exynos_crtc->enabled = true; -  	drm_crtc_vblank_on(crtc);  } @@ -41,20 +35,10 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); -	if (!exynos_crtc->enabled) -		return; - -	/* wait for the completion of page flip. */ -	if (!wait_event_timeout(exynos_crtc->pending_flip_queue, -				(exynos_crtc->event == NULL), HZ/20)) -		exynos_crtc->event = NULL; -  	drm_crtc_vblank_off(crtc);  	if (exynos_crtc->ops->disable)  		exynos_crtc->ops->disable(exynos_crtc); - -	exynos_crtc->enabled = false;  }  static bool @@ -80,18 +64,36 @@ exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)  		exynos_crtc->ops->commit(exynos_crtc);  } -static void exynos_crtc_atomic_begin(struct drm_crtc *crtc) +static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, +				     struct drm_crtc_state *old_crtc_state)  {  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct drm_plane *plane; -	if (crtc->state->event) { -		WARN_ON(drm_crtc_vblank_get(crtc) != 0); -		exynos_crtc->event = crtc->state->event; +	exynos_crtc->event = crtc->state->event; + +	drm_atomic_crtc_for_each_plane(plane, crtc) { +		struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + +		if (exynos_crtc->ops->atomic_begin) +			exynos_crtc->ops->atomic_begin(exynos_crtc, +							exynos_plane);  	}  } -static void exynos_crtc_atomic_flush(struct drm_crtc *crtc) +static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, +				     struct drm_crtc_state *old_crtc_state)  { +	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); +	struct drm_plane *plane; + +	drm_atomic_crtc_for_each_plane(plane, crtc) { +		struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + +		if (exynos_crtc->ops->atomic_flush) +			exynos_crtc->ops->atomic_flush(exynos_crtc, +							exynos_plane); +	}  }  static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { @@ -139,13 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,  	if (!exynos_crtc)  		return ERR_PTR(-ENOMEM); -	init_waitqueue_head(&exynos_crtc->pending_flip_queue); -  	exynos_crtc->pipe = pipe;  	exynos_crtc->type = type;  	exynos_crtc->ops = ops;  	exynos_crtc->ctx = ctx; +	init_waitqueue_head(&exynos_crtc->wait_update); +  	crtc = &exynos_crtc->base;  	private->crtc[pipe] = crtc; @@ -171,11 +173,8 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)  	struct exynos_drm_crtc *exynos_crtc =  		to_exynos_crtc(private->crtc[pipe]); -	if (!exynos_crtc->enabled) -		return -EPERM; -  	if (exynos_crtc->ops->enable_vblank) -		exynos_crtc->ops->enable_vblank(exynos_crtc); +		return exynos_crtc->ops->enable_vblank(exynos_crtc);  	return 0;  } @@ -186,31 +185,34 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)  	struct exynos_drm_crtc *exynos_crtc =  		to_exynos_crtc(private->crtc[pipe]); -	if (!exynos_crtc->enabled) -		return; -  	if (exynos_crtc->ops->disable_vblank)  		exynos_crtc->ops->disable_vblank(exynos_crtc);  } -void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe) +void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)  { -	struct exynos_drm_private *dev_priv = dev->dev_private; -	struct drm_crtc *drm_crtc = dev_priv->crtc[pipe]; -	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc); +	wait_event_timeout(exynos_crtc->wait_update, +			   (atomic_read(&exynos_crtc->pending_update) == 0), +			   msecs_to_jiffies(50)); +} + +void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, +				struct exynos_drm_plane *exynos_plane) +{ +	struct drm_crtc *crtc = &exynos_crtc->base;  	unsigned long flags; -	spin_lock_irqsave(&dev->event_lock, flags); -	if (exynos_crtc->event) { +	exynos_plane->pending_fb = NULL; -		drm_send_vblank_event(dev, -1, exynos_crtc->event); -		drm_vblank_put(dev, pipe); -		wake_up(&exynos_crtc->pending_flip_queue); +	if (atomic_dec_and_test(&exynos_crtc->pending_update)) +		wake_up(&exynos_crtc->wait_update); -	} +	spin_lock_irqsave(&crtc->dev->event_lock, flags); +	if (exynos_crtc->event) +		drm_crtc_send_vblank_event(crtc, exynos_crtc->event);  	exynos_crtc->event = NULL; -	spin_unlock_irqrestore(&dev->event_lock, flags); +	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);  }  void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) @@ -237,7 +239,7 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)  }  int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, -					unsigned int out_type) +				       enum exynos_drm_output_type out_type)  {  	struct drm_crtc *crtc; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 0f3aa70818e3..f87d4abda6f7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -25,12 +25,14 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,  					void *context);  int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);  void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); -void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe); +void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc); +void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, +				   struct exynos_drm_plane *exynos_plane);  void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);  /* This function gets pipe value to crtc device matched with out_type. */  int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, -					unsigned int out_type); +				       enum exynos_drm_output_type out_type);  /*   * This function calls the crtc device(manager)'s te_handler() callback diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c deleted file mode 100644 index cd485c091b30..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ /dev/null @@ -1,286 +0,0 @@ -/* exynos_drm_dmabuf.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/exynos_drm.h> -#include "exynos_drm_dmabuf.h" -#include "exynos_drm_drv.h" -#include "exynos_drm_gem.h" - -#include <linux/dma-buf.h> - -struct exynos_drm_dmabuf_attachment { -	struct sg_table sgt; -	enum dma_data_direction dir; -	bool is_mapped; -}; - -static struct exynos_drm_gem_obj *dma_buf_to_obj(struct dma_buf *buf) -{ -	return to_exynos_gem_obj(buf->priv); -} - -static int exynos_gem_attach_dma_buf(struct dma_buf *dmabuf, -					struct device *dev, -					struct dma_buf_attachment *attach) -{ -	struct exynos_drm_dmabuf_attachment *exynos_attach; - -	exynos_attach = kzalloc(sizeof(*exynos_attach), GFP_KERNEL); -	if (!exynos_attach) -		return -ENOMEM; - -	exynos_attach->dir = DMA_NONE; -	attach->priv = exynos_attach; - -	return 0; -} - -static void exynos_gem_detach_dma_buf(struct dma_buf *dmabuf, -					struct dma_buf_attachment *attach) -{ -	struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; -	struct sg_table *sgt; - -	if (!exynos_attach) -		return; - -	sgt = &exynos_attach->sgt; - -	if (exynos_attach->dir != DMA_NONE) -		dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, -				exynos_attach->dir); - -	sg_free_table(sgt); -	kfree(exynos_attach); -	attach->priv = NULL; -} - -static struct sg_table * -		exynos_gem_map_dma_buf(struct dma_buf_attachment *attach, -					enum dma_data_direction dir) -{ -	struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv; -	struct exynos_drm_gem_obj *gem_obj = dma_buf_to_obj(attach->dmabuf); -	struct drm_device *dev = gem_obj->base.dev; -	struct exynos_drm_gem_buf *buf; -	struct scatterlist *rd, *wr; -	struct sg_table *sgt = NULL; -	unsigned int i; -	int nents, ret; - -	/* just return current sgt if already requested. */ -	if (exynos_attach->dir == dir && exynos_attach->is_mapped) -		return &exynos_attach->sgt; - -	buf = gem_obj->buffer; -	if (!buf) { -		DRM_ERROR("buffer is null.\n"); -		return ERR_PTR(-ENOMEM); -	} - -	sgt = &exynos_attach->sgt; - -	ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL); -	if (ret) { -		DRM_ERROR("failed to alloc sgt.\n"); -		return ERR_PTR(-ENOMEM); -	} - -	mutex_lock(&dev->struct_mutex); - -	rd = buf->sgt->sgl; -	wr = sgt->sgl; -	for (i = 0; i < sgt->orig_nents; ++i) { -		sg_set_page(wr, sg_page(rd), rd->length, rd->offset); -		rd = sg_next(rd); -		wr = sg_next(wr); -	} - -	if (dir != DMA_NONE) { -		nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); -		if (!nents) { -			DRM_ERROR("failed to map sgl with iommu.\n"); -			sg_free_table(sgt); -			sgt = ERR_PTR(-EIO); -			goto err_unlock; -		} -	} - -	exynos_attach->is_mapped = true; -	exynos_attach->dir = dir; -	attach->priv = exynos_attach; - -	DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size); - -err_unlock: -	mutex_unlock(&dev->struct_mutex); -	return sgt; -} - -static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, -						struct sg_table *sgt, -						enum dma_data_direction dir) -{ -	/* Nothing to do. */ -} - -static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, -						unsigned long page_num) -{ -	/* TODO */ - -	return NULL; -} - -static void exynos_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, -						unsigned long page_num, -						void *addr) -{ -	/* TODO */ -} - -static void *exynos_gem_dmabuf_kmap(struct dma_buf *dma_buf, -					unsigned long page_num) -{ -	/* TODO */ - -	return NULL; -} - -static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf, -					unsigned long page_num, void *addr) -{ -	/* TODO */ -} - -static int exynos_gem_dmabuf_mmap(struct dma_buf *dma_buf, -	struct vm_area_struct *vma) -{ -	return -ENOTTY; -} - -static struct dma_buf_ops exynos_dmabuf_ops = { -	.attach			= exynos_gem_attach_dma_buf, -	.detach			= exynos_gem_detach_dma_buf, -	.map_dma_buf		= exynos_gem_map_dma_buf, -	.unmap_dma_buf		= exynos_gem_unmap_dma_buf, -	.kmap			= exynos_gem_dmabuf_kmap, -	.kmap_atomic		= exynos_gem_dmabuf_kmap_atomic, -	.kunmap			= exynos_gem_dmabuf_kunmap, -	.kunmap_atomic		= exynos_gem_dmabuf_kunmap_atomic, -	.mmap			= exynos_gem_dmabuf_mmap, -	.release		= drm_gem_dmabuf_release, -}; - -struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, -				struct drm_gem_object *obj, int flags) -{ -	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); -	DEFINE_DMA_BUF_EXPORT_INFO(exp_info); - -	exp_info.ops = &exynos_dmabuf_ops; -	exp_info.size = exynos_gem_obj->base.size; -	exp_info.flags = flags; -	exp_info.priv = obj; - -	return dma_buf_export(&exp_info); -} - -struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, -				struct dma_buf *dma_buf) -{ -	struct dma_buf_attachment *attach; -	struct sg_table *sgt; -	struct scatterlist *sgl; -	struct exynos_drm_gem_obj *exynos_gem_obj; -	struct exynos_drm_gem_buf *buffer; -	int ret; - -	/* is this one of own objects? */ -	if (dma_buf->ops == &exynos_dmabuf_ops) { -		struct drm_gem_object *obj; - -		obj = dma_buf->priv; - -		/* is it from our device? */ -		if (obj->dev == drm_dev) { -			/* -			 * Importing dmabuf exported from out own gem increases -			 * refcount on gem itself instead of f_count of dmabuf. -			 */ -			drm_gem_object_reference(obj); -			return obj; -		} -	} - -	attach = dma_buf_attach(dma_buf, drm_dev->dev); -	if (IS_ERR(attach)) -		return ERR_PTR(-EINVAL); - -	get_dma_buf(dma_buf); - -	sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); -	if (IS_ERR(sgt)) { -		ret = PTR_ERR(sgt); -		goto err_buf_detach; -	} - -	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); -	if (!buffer) { -		ret = -ENOMEM; -		goto err_unmap_attach; -	} - -	exynos_gem_obj = exynos_drm_gem_init(drm_dev, dma_buf->size); -	if (!exynos_gem_obj) { -		ret = -ENOMEM; -		goto err_free_buffer; -	} - -	sgl = sgt->sgl; - -	buffer->size = dma_buf->size; -	buffer->dma_addr = sg_dma_address(sgl); - -	if (sgt->nents == 1) { -		/* always physically continuous memory if sgt->nents is 1. */ -		exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; -	} else { -		/* -		 * this case could be CONTIG or NONCONTIG type but for now -		 * sets NONCONTIG. -		 * TODO. we have to find a way that exporter can notify -		 * the type of its own buffer to importer. -		 */ -		exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; -	} - -	exynos_gem_obj->buffer = buffer; -	buffer->sgt = sgt; -	exynos_gem_obj->base.import_attach = attach; - -	DRM_DEBUG_PRIME("dma_addr = %pad, size = 0x%lx\n", &buffer->dma_addr, -								buffer->size); - -	return &exynos_gem_obj->base; - -err_free_buffer: -	kfree(buffer); -	buffer = NULL; -err_unmap_attach: -	dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); -err_buf_detach: -	dma_buf_detach(dma_buf, attach); -	dma_buf_put(dma_buf); - -	return ERR_PTR(ret); -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h deleted file mode 100644 index 886de9ff484d..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h +++ /dev/null @@ -1,20 +0,0 @@ -/* exynos_drm_dmabuf.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Author: Inki Dae <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#ifndef _EXYNOS_DRM_DMABUF_H_ -#define _EXYNOS_DRM_DMABUF_H_ - -struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, -				struct drm_gem_object *obj, int flags); - -struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, -						struct dma_buf *dma_buf); -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 7cb6595c1894..c748b8790de3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -20,26 +20,24 @@  #include <video/of_videomode.h>  #include <video/videomode.h> -#include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h"  struct exynos_dpi { -	struct exynos_drm_display display; +	struct drm_encoder encoder;  	struct device *dev;  	struct device_node *panel_node;  	struct drm_panel *panel;  	struct drm_connector connector; -	struct drm_encoder *encoder;  	struct videomode *vm; -	int dpms_mode;  };  #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector) -static inline struct exynos_dpi *display_to_dpi(struct exynos_drm_display *d) +static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)  { -	return container_of(d, struct exynos_dpi, display); +	return container_of(e, struct exynos_dpi, encoder);  }  static enum drm_connector_status @@ -99,7 +97,7 @@ exynos_dpi_best_encoder(struct drm_connector *connector)  {  	struct exynos_dpi *ctx = connector_to_dpi(connector); -	return ctx->encoder; +	return &ctx->encoder;  }  static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { @@ -107,15 +105,12 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {  	.best_encoder = exynos_dpi_best_encoder,  }; -static int exynos_dpi_create_connector(struct exynos_drm_display *display, -				       struct drm_encoder *encoder) +static int exynos_dpi_create_connector(struct drm_encoder *encoder)  { -	struct exynos_dpi *ctx = display_to_dpi(display); +	struct exynos_dpi *ctx = encoder_to_dpi(encoder);  	struct drm_connector *connector = &ctx->connector;  	int ret; -	ctx->encoder = encoder; -  	connector->polled = DRM_CONNECTOR_POLL_HPD;  	ret = drm_connector_init(encoder->dev, connector, @@ -133,46 +128,48 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,  	return 0;  } -static void exynos_dpi_poweron(struct exynos_dpi *ctx) +static bool exynos_dpi_mode_fixup(struct drm_encoder *encoder, +				  const struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void exynos_dpi_mode_set(struct drm_encoder *encoder, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode)  { +} + +static void exynos_dpi_enable(struct drm_encoder *encoder) +{ +	struct exynos_dpi *ctx = encoder_to_dpi(encoder); +  	if (ctx->panel) {  		drm_panel_prepare(ctx->panel);  		drm_panel_enable(ctx->panel);  	}  } -static void exynos_dpi_poweroff(struct exynos_dpi *ctx) +static void exynos_dpi_disable(struct drm_encoder *encoder)  { +	struct exynos_dpi *ctx = encoder_to_dpi(encoder); +  	if (ctx->panel) {  		drm_panel_disable(ctx->panel);  		drm_panel_unprepare(ctx->panel);  	}  } -static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode) -{ -	struct exynos_dpi *ctx = display_to_dpi(display); - -	switch (mode) { -	case DRM_MODE_DPMS_ON: -		if (ctx->dpms_mode != DRM_MODE_DPMS_ON) -				exynos_dpi_poweron(ctx); -			break; -	case DRM_MODE_DPMS_STANDBY: -	case DRM_MODE_DPMS_SUSPEND: -	case DRM_MODE_DPMS_OFF: -		if (ctx->dpms_mode == DRM_MODE_DPMS_ON) -			exynos_dpi_poweroff(ctx); -		break; -	default: -		break; -	} -	ctx->dpms_mode = mode; -} +static struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = { +	.mode_fixup = exynos_dpi_mode_fixup, +	.mode_set = exynos_dpi_mode_set, +	.enable = exynos_dpi_enable, +	.disable = exynos_dpi_disable, +}; -static struct exynos_drm_display_ops exynos_dpi_display_ops = { -	.create_connector = exynos_dpi_create_connector, -	.dpms = exynos_dpi_dpms +static struct drm_encoder_funcs exynos_dpi_encoder_funcs = { +	.destroy = drm_encoder_cleanup,  };  /* of_* functions will be removed after merge of of_graph patches */ @@ -299,7 +296,34 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)  	return 0;  } -struct exynos_drm_display *exynos_dpi_probe(struct device *dev) +int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) +{ +	int ret; + +	ret = exynos_drm_crtc_get_pipe_from_type(dev, EXYNOS_DISPLAY_TYPE_LCD); +	if (ret < 0) +		return ret; + +	encoder->possible_crtcs = 1 << ret; + +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + +	drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs); + +	ret = exynos_dpi_create_connector(encoder); +	if (ret) { +		DRM_ERROR("failed to create connector ret = %d\n", ret); +		drm_encoder_cleanup(encoder); +		return ret; +	} + +	return 0; +} + +struct drm_encoder *exynos_dpi_probe(struct device *dev)  {  	struct exynos_dpi *ctx;  	int ret; @@ -308,10 +332,7 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)  	if (!ctx)  		return ERR_PTR(-ENOMEM); -	ctx->display.type = EXYNOS_DISPLAY_TYPE_LCD; -	ctx->display.ops = &exynos_dpi_display_ops;  	ctx->dev = dev; -	ctx->dpms_mode = DRM_MODE_DPMS_OFF;  	ret = exynos_dpi_parse_dt(ctx);  	if (ret < 0) { @@ -325,14 +346,14 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)  			return ERR_PTR(-EPROBE_DEFER);  	} -	return &ctx->display; +	return &ctx->encoder;  } -int exynos_dpi_remove(struct exynos_drm_display *display) +int exynos_dpi_remove(struct drm_encoder *encoder)  { -	struct exynos_dpi *ctx = display_to_dpi(display); +	struct exynos_dpi *ctx = encoder_to_dpi(encoder); -	exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF); +	exynos_dpi_disable(&ctx->encoder);  	if (ctx->panel)  		drm_panel_detach(ctx->panel); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 63a68c60a353..831d2e4cacf9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -13,6 +13,8 @@  #include <linux/pm_runtime.h>  #include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>  #include <drm/drm_crtc_helper.h>  #include <linux/component.h> @@ -21,13 +23,11 @@  #include "exynos_drm_drv.h"  #include "exynos_drm_crtc.h" -#include "exynos_drm_encoder.h"  #include "exynos_drm_fbdev.h"  #include "exynos_drm_fb.h"  #include "exynos_drm_gem.h"  #include "exynos_drm_plane.h"  #include "exynos_drm_vidi.h" -#include "exynos_drm_dmabuf.h"  #include "exynos_drm_g2d.h"  #include "exynos_drm_ipp.h"  #include "exynos_drm_iommu.h" @@ -38,15 +38,112 @@  #define DRIVER_MAJOR	1  #define DRIVER_MINOR	0 +struct exynos_atomic_commit { +	struct work_struct	work; +	struct drm_device	*dev; +	struct drm_atomic_state *state; +	u32			crtcs; +}; + +static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state) +{ +	struct drm_crtc_state *crtc_state; +	struct drm_crtc *crtc; +	int i, ret; + +	for_each_crtc_in_state(state, crtc, crtc_state, i) { +		struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + +		if (!crtc->state->enable) +			continue; + +		ret = drm_crtc_vblank_get(crtc); +		if (ret) +			continue; + +		exynos_drm_crtc_wait_pending_update(exynos_crtc); +		drm_crtc_vblank_put(crtc); +	} +} + +static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) +{ +	struct drm_device *dev = commit->dev; +	struct exynos_drm_private *priv = dev->dev_private; +	struct drm_atomic_state *state = commit->state; +	struct drm_plane *plane; +	struct drm_crtc *crtc; +	struct drm_plane_state *plane_state; +	struct drm_crtc_state *crtc_state; +	int i; + +	drm_atomic_helper_commit_modeset_disables(dev, state); + +	drm_atomic_helper_commit_modeset_enables(dev, state); + +	/* +	 * Exynos can't update planes with CRTCs and encoders disabled, +	 * its updates routines, specially for FIMD, requires the clocks +	 * to be enabled. So it is necessary to handle the modeset operations +	 * *before* the commit_planes() step, this way it will always +	 * have the relevant clocks enabled to perform the update. +	 */ + +	for_each_crtc_in_state(state, crtc, crtc_state, i) { +		struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + +		atomic_set(&exynos_crtc->pending_update, 0); +	} + +	for_each_plane_in_state(state, plane, plane_state, i) { +		struct exynos_drm_crtc *exynos_crtc = +						to_exynos_crtc(plane->crtc); + +		if (!plane->crtc) +			continue; + +		atomic_inc(&exynos_crtc->pending_update); +	} + +	drm_atomic_helper_commit_planes(dev, state); + +	exynos_atomic_wait_for_commit(state); + +	drm_atomic_helper_cleanup_planes(dev, state); + +	drm_atomic_state_free(state); + +	spin_lock(&priv->lock); +	priv->pending &= ~commit->crtcs; +	spin_unlock(&priv->lock); + +	wake_up_all(&priv->wait); + +	kfree(commit); +} + +static void exynos_drm_atomic_work(struct work_struct *work) +{ +	struct exynos_atomic_commit *commit = container_of(work, +				struct exynos_atomic_commit, work); + +	exynos_atomic_commit_complete(commit); +} +  static int exynos_drm_load(struct drm_device *dev, unsigned long flags)  {  	struct exynos_drm_private *private; -	int ret; +	struct drm_encoder *encoder; +	unsigned int clone_mask; +	int cnt, ret;  	private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);  	if (!private)  		return -ENOMEM; +	init_waitqueue_head(&private->wait); +	spin_lock_init(&private->lock); +  	dev_set_drvdata(dev->dev, dev);  	dev->dev_private = (void *)private; @@ -67,7 +164,13 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)  	exynos_drm_mode_config_init(dev);  	/* setup possible_clones. */ -	exynos_drm_encoder_setup(dev); +	cnt = 0; +	clone_mask = 0; +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) +		clone_mask |= (1 << (cnt++)); + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) +		encoder->possible_clones = clone_mask;  	platform_set_drvdata(dev->platformdev, dev); @@ -143,6 +246,64 @@ static int exynos_drm_unload(struct drm_device *dev)  	return 0;  } +static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs) +{ +	bool pending; + +	spin_lock(&priv->lock); +	pending = priv->pending & crtcs; +	spin_unlock(&priv->lock); + +	return pending; +} + +int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, +			 bool async) +{ +	struct exynos_drm_private *priv = dev->dev_private; +	struct exynos_atomic_commit *commit; +	int i, ret; + +	commit = kzalloc(sizeof(*commit), GFP_KERNEL); +	if (!commit) +		return -ENOMEM; + +	ret = drm_atomic_helper_prepare_planes(dev, state); +	if (ret) { +		kfree(commit); +		return ret; +	} + +	/* This is the point of no return */ + +	INIT_WORK(&commit->work, exynos_drm_atomic_work); +	commit->dev = dev; +	commit->state = state; + +	/* Wait until all affected CRTCs have completed previous commits and +	 * mark them as pending. +	 */ +	for (i = 0; i < dev->mode_config.num_crtc; ++i) { +		if (state->crtcs[i]) +			commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); +	} + +	wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs)); + +	spin_lock(&priv->lock); +	priv->pending |= commit->crtcs; +	spin_unlock(&priv->lock); + +	drm_atomic_helper_swap_state(dev, state); + +	if (async) +		schedule_work(&commit->work); +	else +		exynos_atomic_commit_complete(commit); + +	return 0; +} +  static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)  {  	struct drm_connector *connector; @@ -242,25 +403,25 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = {  static const struct drm_ioctl_desc exynos_ioctls[] = {  	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, +			DRM_UNLOCKED | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,  			DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, -			exynos_drm_gem_get_ioctl, DRM_UNLOCKED), -	DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, -			vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, -			exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, -			exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, -			exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, -			exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, -			exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, -			exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH), -	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, -			exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH), +	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), +	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl, +			DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),  };  static const struct file_operations exynos_drm_driver_fops = { @@ -277,11 +438,10 @@ static const struct file_operations exynos_drm_driver_fops = {  };  static struct drm_driver exynos_drm_driver = { -	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, +	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME +				  | DRIVER_ATOMIC | DRIVER_RENDER,  	.load			= exynos_drm_load,  	.unload			= exynos_drm_unload, -	.suspend		= exynos_drm_suspend, -	.resume			= exynos_drm_resume,  	.open			= exynos_drm_open,  	.preclose		= exynos_drm_preclose,  	.lastclose		= exynos_drm_lastclose, @@ -297,8 +457,12 @@ static struct drm_driver exynos_drm_driver = {  	.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_export	= exynos_dmabuf_prime_export, -	.gem_prime_import	= exynos_dmabuf_prime_import, +	.gem_prime_export	= drm_gem_prime_export, +	.gem_prime_import	= drm_gem_prime_import, +	.gem_prime_get_sg_table	= exynos_drm_gem_prime_get_sg_table, +	.gem_prime_import_sg_table	= exynos_drm_gem_prime_import_sg_table, +	.gem_prime_vmap		= exynos_drm_gem_prime_vmap, +	.gem_prime_vunmap	= exynos_drm_gem_prime_vunmap,  	.ioctls			= exynos_ioctls,  	.num_ioctls		= ARRAY_SIZE(exynos_ioctls),  	.fops			= &exynos_drm_driver_fops, @@ -345,9 +509,6 @@ static struct platform_driver exynos_drm_platform_driver;   * because connector requires pipe number of its crtc during initialization.   */  static struct platform_driver *const exynos_drm_kms_drivers[] = { -#ifdef CONFIG_DRM_EXYNOS_VIDI -	&vidi_driver, -#endif  #ifdef CONFIG_DRM_EXYNOS_FIMD  	&fimd_driver,  #endif @@ -370,6 +531,9 @@ static struct platform_driver *const exynos_drm_kms_drivers[] = {  	&mixer_driver,  	&hdmi_driver,  #endif +#ifdef CONFIG_DRM_EXYNOS_VIDI +	&vidi_driver, +#endif  };  static struct platform_driver *const exynos_drm_non_kms_drivers[] = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index dd00f160c1e5..b7ba21dfb696 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -44,23 +44,14 @@ enum exynos_drm_output_type {   *	- the unit is screen coordinates.   * @src_y: offset y on a framebuffer to be displayed.   *	- the unit is screen coordinates. - * @src_width: width of a partial image to be displayed from framebuffer. - * @src_height: height of a partial image to be displayed from framebuffer. - * @fb_width: width of a framebuffer. - * @fb_height: height of a framebuffer. + * @src_w: width of a partial image to be displayed from framebuffer. + * @src_h: height of a partial image to be displayed from framebuffer.   * @crtc_x: offset x on hardware screen.   * @crtc_y: offset y on hardware screen. - * @crtc_width: window width to be displayed (hardware screen). - * @crtc_height: window height to be displayed (hardware screen). - * @mode_width: width of screen mode. - * @mode_height: height of screen mode. + * @crtc_w: window width to be displayed (hardware screen). + * @crtc_h: window height to be displayed (hardware screen).   * @h_ratio: horizontal scaling ratio, 16.16 fixed point   * @v_ratio: vertical scaling ratio, 16.16 fixed point - * @refresh: refresh rate. - * @scan_flag: interlace or progressive way. - *	(it could be DRM_MODE_FLAG_*) - * @bpp: pixel size.(in bit) - * @pixel_format: fourcc pixel format of this overlay   * @dma_addr: array of bus(accessed by dma) address to the memory region   *	      allocated for a overlay.   * @zpos: order of overlay layer(z position). @@ -73,73 +64,17 @@ struct exynos_drm_plane {  	struct drm_plane base;  	unsigned int src_x;  	unsigned int src_y; -	unsigned int src_width; -	unsigned int src_height; -	unsigned int fb_width; -	unsigned int fb_height; +	unsigned int src_w; +	unsigned int src_h;  	unsigned int crtc_x;  	unsigned int crtc_y; -	unsigned int crtc_width; -	unsigned int crtc_height; -	unsigned int mode_width; -	unsigned int mode_height; +	unsigned int crtc_w; +	unsigned int crtc_h;  	unsigned int h_ratio;  	unsigned int v_ratio; -	unsigned int refresh; -	unsigned int scan_flag; -	unsigned int bpp; -	unsigned int pitch; -	uint32_t pixel_format;  	dma_addr_t dma_addr[MAX_FB_BUFFER];  	unsigned int zpos; -}; - -/* - * Exynos DRM Display Structure. - *	- this structure is common to analog tv, digital tv and lcd panel. - * - * @create_connector: initialize and register a new connector - * @remove: cleans up the display for removal - * @mode_fixup: fix mode data comparing to hw specific display mode. - * @mode_set: convert drm_display_mode to hw specific display mode and - *	      would be called by encoder->mode_set(). - * @check_mode: check if mode is valid or not. - * @dpms: display device on or off. - * @commit: apply changes to hw - */ -struct exynos_drm_display; -struct exynos_drm_display_ops { -	int (*create_connector)(struct exynos_drm_display *display, -				struct drm_encoder *encoder); -	void (*remove)(struct exynos_drm_display *display); -	void (*mode_fixup)(struct exynos_drm_display *display, -				struct drm_connector *connector, -				const struct drm_display_mode *mode, -				struct drm_display_mode *adjusted_mode); -	void (*mode_set)(struct exynos_drm_display *display, -				struct drm_display_mode *mode); -	int (*check_mode)(struct exynos_drm_display *display, -				struct drm_display_mode *mode); -	void (*dpms)(struct exynos_drm_display *display, int mode); -	void (*commit)(struct exynos_drm_display *display); -}; - -/* - * Exynos drm display structure, maps 1:1 with an encoder/connector - * - * @list: the list entry for this manager - * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. - * @encoder: encoder object this display maps to - * @connector: connector object this display maps to - * @ops: pointer to callbacks for exynos drm specific functionality - * @ctx: A pointer to the display's implementation specific context - */ -struct exynos_drm_display { -	struct list_head list; -	enum exynos_drm_output_type type; -	struct drm_encoder *encoder; -	struct drm_connector *connector; -	struct exynos_drm_display_ops *ops; +	struct drm_framebuffer *pending_fb;  };  /* @@ -153,8 +88,10 @@ struct exynos_drm_display {   * @disable_vblank: specific driver callback for disabling vblank interrupt.   * @wait_for_vblank: wait for vblank interrupt to make sure that   *	hardware overlay is updated. - * @win_commit: apply hardware specific overlay data to registers. - * @win_disable: disable hardware specific overlay. + * @atomic_begin: prepare a window to receive a update + * @atomic_flush: mark the end of a window update + * @update_plane: apply hardware specific overlay data to registers. + * @disable_plane: disable hardware specific overlay.   * @te_handler: trigger to transfer video image at the tearing effect   *	synchronization signal if there is a page flip request.   * @clock_enable: optional function enabling/disabling display domain clock, @@ -173,11 +110,16 @@ struct exynos_drm_crtc_ops {  	int (*enable_vblank)(struct exynos_drm_crtc *crtc);  	void (*disable_vblank)(struct exynos_drm_crtc *crtc);  	void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); -	void (*win_commit)(struct exynos_drm_crtc *crtc, unsigned int zpos); -	void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos); +	void (*atomic_begin)(struct exynos_drm_crtc *crtc, +			      struct exynos_drm_plane *plane); +	void (*update_plane)(struct exynos_drm_crtc *crtc, +			     struct exynos_drm_plane *plane); +	void (*disable_plane)(struct exynos_drm_crtc *crtc, +			      struct exynos_drm_plane *plane); +	void (*atomic_flush)(struct exynos_drm_crtc *crtc, +			      struct exynos_drm_plane *plane);  	void (*te_handler)(struct exynos_drm_crtc *crtc);  	void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); -	void (*clear_channels)(struct exynos_drm_crtc *crtc);  };  /* @@ -194,6 +136,8 @@ struct exynos_drm_crtc_ops {   *	this pipe value.   * @enabled: if the crtc is enabled or not   * @event: vblank event that is currently queued for flip + * @wait_update: wait all pending planes updates to finish + * @pending_update: number of pending plane updates in this crtc   * @ops: pointer to callbacks for exynos drm specific functionality   * @ctx: A pointer to the crtc's implementation specific context   */ @@ -201,9 +145,9 @@ struct exynos_drm_crtc {  	struct drm_crtc			base;  	enum exynos_drm_output_type	type;  	unsigned int			pipe; -	bool				enabled; -	wait_queue_head_t		pending_flip_queue;  	struct drm_pending_vblank_event	*event; +	wait_queue_head_t		wait_update; +	atomic_t			pending_update;  	const struct exynos_drm_crtc_ops	*ops;  	void				*ctx;  }; @@ -229,6 +173,9 @@ struct drm_exynos_file_private {   * @da_space_size: size of device address space.   *	if 0 then default value is used for it.   * @pipe: the pipe number for this crtc/manager. + * @pending: the crtcs that have pending updates to finish + * @lock: protect access to @pending + * @wait: wait an atomic commit to finish   */  struct exynos_drm_private {  	struct drm_fb_helper *fb_helper; @@ -244,6 +191,11 @@ struct exynos_drm_private {  	unsigned long da_space_size;  	unsigned int pipe; + +	/* for atomic commit */ +	u32			pending; +	spinlock_t		lock; +	wait_queue_head_t	wait;  };  /* @@ -285,20 +237,26 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);  void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);  #ifdef CONFIG_DRM_EXYNOS_DPI -struct exynos_drm_display * exynos_dpi_probe(struct device *dev); -int exynos_dpi_remove(struct exynos_drm_display *display); +struct drm_encoder *exynos_dpi_probe(struct device *dev); +int exynos_dpi_remove(struct drm_encoder *encoder); +int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder);  #else -static inline struct exynos_drm_display * +static inline struct drm_encoder *  exynos_dpi_probe(struct device *dev) { return NULL; } -static inline int exynos_dpi_remove(struct exynos_drm_display *display) +static inline int exynos_dpi_remove(struct drm_encoder *encoder) +{ +	return 0; +} +static inline int exynos_dpi_bind(struct drm_device *dev, +				  struct drm_encoder *encoder)  {  	return 0;  }  #endif -/* This function creates a encoder and a connector, and initializes them. */ -int exynos_drm_create_enc_conn(struct drm_device *dev, -				struct exynos_drm_display *display); +int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, +			 bool async); +  extern struct platform_driver fimd_driver;  extern struct platform_driver exynos5433_decon_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 0e58b36cb8c2..12b03b364703 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -259,7 +259,7 @@ struct exynos_dsi_driver_data {  };  struct exynos_dsi { -	struct exynos_drm_display display; +	struct drm_encoder encoder;  	struct mipi_dsi_host dsi_host;  	struct drm_connector connector;  	struct device_node *panel_node; @@ -295,9 +295,9 @@ struct exynos_dsi {  #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)  #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) -static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d) +static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)  { -	return container_of(d, struct exynos_dsi, display); +	return container_of(e, struct exynos_dsi, encoder);  }  enum reg_idx { @@ -1272,7 +1272,7 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)  static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)  {  	struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id; -	struct drm_encoder *encoder = dsi->display.encoder; +	struct drm_encoder *encoder = &dsi->encoder;  	if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)  		exynos_drm_crtc_te_handler(encoder->crtc); @@ -1518,16 +1518,17 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)  		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);  } -static int exynos_dsi_enable(struct exynos_dsi *dsi) +static void exynos_dsi_enable(struct drm_encoder *encoder)  { +	struct exynos_dsi *dsi = encoder_to_dsi(encoder);  	int ret;  	if (dsi->state & DSIM_STATE_ENABLED) -		return 0; +		return;  	ret = exynos_dsi_poweron(dsi);  	if (ret < 0) -		return ret; +		return;  	dsi->state |= DSIM_STATE_ENABLED; @@ -1535,7 +1536,7 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)  	if (ret < 0) {  		dsi->state &= ~DSIM_STATE_ENABLED;  		exynos_dsi_poweroff(dsi); -		return ret; +		return;  	}  	exynos_dsi_set_display_mode(dsi); @@ -1547,16 +1548,16 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)  		exynos_dsi_set_display_enable(dsi, false);  		drm_panel_unprepare(dsi->panel);  		exynos_dsi_poweroff(dsi); -		return ret; +		return;  	}  	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; - -	return 0;  } -static void exynos_dsi_disable(struct exynos_dsi *dsi) +static void exynos_dsi_disable(struct drm_encoder *encoder)  { +	struct exynos_dsi *dsi = encoder_to_dsi(encoder); +  	if (!(dsi->state & DSIM_STATE_ENABLED))  		return; @@ -1571,26 +1572,6 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)  	exynos_dsi_poweroff(dsi);  } -static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) -{ -	struct exynos_dsi *dsi = display_to_dsi(display); - -	if (dsi->panel) { -		switch (mode) { -		case DRM_MODE_DPMS_ON: -			exynos_dsi_enable(dsi); -			break; -		case DRM_MODE_DPMS_STANDBY: -		case DRM_MODE_DPMS_SUSPEND: -		case DRM_MODE_DPMS_OFF: -			exynos_dsi_disable(dsi); -			break; -		default: -			break; -		} -	} -} -  static enum drm_connector_status  exynos_dsi_detect(struct drm_connector *connector, bool force)  { @@ -1601,10 +1582,10 @@ exynos_dsi_detect(struct drm_connector *connector, bool force)  		if (dsi->panel)  			drm_panel_attach(dsi->panel, &dsi->connector);  	} else if (!dsi->panel_node) { -		struct exynos_drm_display *display; +		struct drm_encoder *encoder; -		display = platform_get_drvdata(to_platform_device(dsi->dev)); -		exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); +		encoder = platform_get_drvdata(to_platform_device(dsi->dev)); +		exynos_dsi_disable(encoder);  		drm_panel_detach(dsi->panel);  		dsi->panel = NULL;  	} @@ -1647,7 +1628,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector)  {  	struct exynos_dsi *dsi = connector_to_dsi(connector); -	return dsi->display.encoder; +	return &dsi->encoder;  }  static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { @@ -1655,10 +1636,9 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {  	.best_encoder = exynos_dsi_best_encoder,  }; -static int exynos_dsi_create_connector(struct exynos_drm_display *display, -				       struct drm_encoder *encoder) +static int exynos_dsi_create_connector(struct drm_encoder *encoder)  { -	struct exynos_dsi *dsi = display_to_dsi(display); +	struct exynos_dsi *dsi = encoder_to_dsi(encoder);  	struct drm_connector *connector = &dsi->connector;  	int ret; @@ -1679,26 +1659,40 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,  	return 0;  } -static void exynos_dsi_mode_set(struct exynos_drm_display *display, -			 struct drm_display_mode *mode) +static bool exynos_dsi_mode_fixup(struct drm_encoder *encoder, +				  const struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode)  { -	struct exynos_dsi *dsi = display_to_dsi(display); -	struct videomode *vm = &dsi->vm; +	return true; +} -	vm->hactive = mode->hdisplay; -	vm->vactive = mode->vdisplay; -	vm->vfront_porch = mode->vsync_start - mode->vdisplay; -	vm->vback_porch = mode->vtotal - mode->vsync_end; -	vm->vsync_len = mode->vsync_end - mode->vsync_start; -	vm->hfront_porch = mode->hsync_start - mode->hdisplay; -	vm->hback_porch = mode->htotal - mode->hsync_end; -	vm->hsync_len = mode->hsync_end - mode->hsync_start; +static void exynos_dsi_mode_set(struct drm_encoder *encoder, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct exynos_dsi *dsi = encoder_to_dsi(encoder); +	struct videomode *vm = &dsi->vm; +	struct drm_display_mode *m = adjusted_mode; + +	vm->hactive = m->hdisplay; +	vm->vactive = m->vdisplay; +	vm->vfront_porch = m->vsync_start - m->vdisplay; +	vm->vback_porch = m->vtotal - m->vsync_end; +	vm->vsync_len = m->vsync_end - m->vsync_start; +	vm->hfront_porch = m->hsync_start - m->hdisplay; +	vm->hback_porch = m->htotal - m->hsync_end; +	vm->hsync_len = m->hsync_end - m->hsync_start;  } -static struct exynos_drm_display_ops exynos_dsi_display_ops = { -	.create_connector = exynos_dsi_create_connector, +static struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = { +	.mode_fixup = exynos_dsi_mode_fixup,  	.mode_set = exynos_dsi_mode_set, -	.dpms = exynos_dsi_dpms +	.enable = exynos_dsi_enable, +	.disable = exynos_dsi_disable, +}; + +static struct drm_encoder_funcs exynos_dsi_encoder_funcs = { +	.destroy = drm_encoder_cleanup,  };  MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); @@ -1821,22 +1815,35 @@ end:  static int exynos_dsi_bind(struct device *dev, struct device *master,  				void *data)  { -	struct exynos_drm_display *display = dev_get_drvdata(dev); -	struct exynos_dsi *dsi = display_to_dsi(display); +	struct drm_encoder *encoder = dev_get_drvdata(dev); +	struct exynos_dsi *dsi = encoder_to_dsi(encoder);  	struct drm_device *drm_dev = data;  	struct drm_bridge *bridge;  	int ret; -	ret = exynos_drm_create_enc_conn(drm_dev, display); +	ret = exynos_drm_crtc_get_pipe_from_type(drm_dev, +						  EXYNOS_DISPLAY_TYPE_LCD); +	if (ret < 0) +		return ret; + +	encoder->possible_crtcs = 1 << ret; + +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + +	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs); + +	ret = exynos_dsi_create_connector(encoder);  	if (ret) { -		DRM_ERROR("Encoder create [%d] failed with %d\n", -			  display->type, ret); +		DRM_ERROR("failed to create connector ret = %d\n", ret); +		drm_encoder_cleanup(encoder);  		return ret;  	}  	bridge = of_drm_find_bridge(dsi->bridge_node);  	if (bridge) { -		display->encoder->bridge = bridge;  		drm_bridge_attach(drm_dev, bridge);  	} @@ -1846,10 +1853,10 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,  static void exynos_dsi_unbind(struct device *dev, struct device *master,  				void *data)  { -	struct exynos_drm_display *display = dev_get_drvdata(dev); -	struct exynos_dsi *dsi = display_to_dsi(display); +	struct drm_encoder *encoder = dev_get_drvdata(dev); +	struct exynos_dsi *dsi = encoder_to_dsi(encoder); -	exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); +	exynos_dsi_disable(encoder);  	mipi_dsi_host_unregister(&dsi->dsi_host);  } @@ -1870,9 +1877,6 @@ static int exynos_dsi_probe(struct platform_device *pdev)  	if (!dsi)  		return -ENOMEM; -	dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD; -	dsi->display.ops = &exynos_dsi_display_ops; -  	/* To be checked as invalid one */  	dsi->te_gpio = -ENOENT; @@ -1948,7 +1952,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)  		return ret;  	} -	platform_set_drvdata(pdev, &dsi->display); +	platform_set_drvdata(pdev, &dsi->encoder);  	return component_add(dev, &exynos_dsi_component_ops);  } diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c deleted file mode 100644 index 7b89fd520e45..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ /dev/null @@ -1,174 +0,0 @@ -/* exynos_drm_encoder.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - *	Inki Dae <[email protected]> - *	Joonyoung Shim <[email protected]> - *	Seung-Woo Kim <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" - -#define to_exynos_encoder(x)	container_of(x, struct exynos_drm_encoder,\ -				drm_encoder) - -/* - * exynos specific encoder structure. - * - * @drm_encoder: encoder object. - * @display: the display structure that maps to this encoder - */ -struct exynos_drm_encoder { -	struct drm_encoder		drm_encoder; -	struct exynos_drm_display	*display; -}; - -static bool -exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, -			       const struct drm_display_mode *mode, -			       struct drm_display_mode *adjusted_mode) -{ -	struct drm_device *dev = encoder->dev; -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); -	struct exynos_drm_display *display = exynos_encoder->display; -	struct drm_connector *connector; - -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -		if (connector->encoder != encoder) -			continue; - -		if (display->ops->mode_fixup) -			display->ops->mode_fixup(display, connector, mode, -					adjusted_mode); -	} - -	return true; -} - -static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, -					 struct drm_display_mode *mode, -					 struct drm_display_mode *adjusted_mode) -{ -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); -	struct exynos_drm_display *display = exynos_encoder->display; - -	if (display->ops->mode_set) -		display->ops->mode_set(display, adjusted_mode); -} - -static void exynos_drm_encoder_enable(struct drm_encoder *encoder) -{ -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); -	struct exynos_drm_display *display = exynos_encoder->display; - -	if (display->ops->dpms) -		display->ops->dpms(display, DRM_MODE_DPMS_ON); - -	if (display->ops->commit) -		display->ops->commit(display); -} - -static void exynos_drm_encoder_disable(struct drm_encoder *encoder) -{ -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); -	struct exynos_drm_display *display = exynos_encoder->display; - -	if (display->ops->dpms) -		display->ops->dpms(display, DRM_MODE_DPMS_OFF); -} - -static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { -	.mode_fixup	= exynos_drm_encoder_mode_fixup, -	.mode_set	= exynos_drm_encoder_mode_set, -	.enable		= exynos_drm_encoder_enable, -	.disable	= exynos_drm_encoder_disable, -}; - -static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) -{ -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - -	drm_encoder_cleanup(encoder); -	kfree(exynos_encoder); -} - -static struct drm_encoder_funcs exynos_encoder_funcs = { -	.destroy = exynos_drm_encoder_destroy, -}; - -static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) -{ -	struct drm_encoder *clone; -	struct drm_device *dev = encoder->dev; -	struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); -	struct exynos_drm_display *display = exynos_encoder->display; -	unsigned int clone_mask = 0; -	int cnt = 0; - -	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { -		switch (display->type) { -		case EXYNOS_DISPLAY_TYPE_LCD: -		case EXYNOS_DISPLAY_TYPE_HDMI: -		case EXYNOS_DISPLAY_TYPE_VIDI: -			clone_mask |= (1 << (cnt++)); -			break; -		default: -			continue; -		} -	} - -	return clone_mask; -} - -void exynos_drm_encoder_setup(struct drm_device *dev) -{ -	struct drm_encoder *encoder; - -	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) -		encoder->possible_clones = exynos_drm_encoder_clones(encoder); -} - -struct drm_encoder * -exynos_drm_encoder_create(struct drm_device *dev, -			   struct exynos_drm_display *display, -			   unsigned long possible_crtcs) -{ -	struct drm_encoder *encoder; -	struct exynos_drm_encoder *exynos_encoder; - -	if (!possible_crtcs) -		return NULL; - -	exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); -	if (!exynos_encoder) -		return NULL; - -	exynos_encoder->display = display; -	encoder = &exynos_encoder->drm_encoder; -	encoder->possible_crtcs = possible_crtcs; - -	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); - -	drm_encoder_init(dev, encoder, &exynos_encoder_funcs, -			DRM_MODE_ENCODER_TMDS); - -	drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); - -	DRM_DEBUG_KMS("encoder has been created\n"); - -	return encoder; -} - -struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder) -{ -	return to_exynos_encoder(encoder)->display; -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h deleted file mode 100644 index 26305d8dd93a..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - *	Inki Dae <[email protected]> - *	Joonyoung Shim <[email protected]> - *	Seung-Woo Kim <[email protected]> - * - * This program is free software; you can redistribute  it and/or modify it - * under  the terms of  the GNU General  Public License as published by the - * Free Software Foundation;  either version 2 of the  License, or (at your - * option) any later version. - */ - -#ifndef _EXYNOS_DRM_ENCODER_H_ -#define _EXYNOS_DRM_ENCODER_H_ - -void exynos_drm_encoder_setup(struct drm_device *dev); -struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, -			struct exynos_drm_display *mgr, -			unsigned long possible_crtcs); -struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder); - -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 2b6320e6eae2..084280859589 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -23,7 +23,6 @@  #include "exynos_drm_drv.h"  #include "exynos_drm_fb.h"  #include "exynos_drm_fbdev.h" -#include "exynos_drm_gem.h"  #include "exynos_drm_iommu.h"  #include "exynos_drm_crtc.h" @@ -33,12 +32,10 @@   * exynos specific framebuffer structure.   *   * @fb: drm framebuffer obejct. - * @buf_cnt: a buffer count to drm framebuffer.   * @exynos_gem_obj: array of exynos specific gem object containing a gem object.   */  struct exynos_drm_fb {  	struct drm_framebuffer		fb; -	unsigned int			buf_cnt;  	struct exynos_drm_gem_obj	*exynos_gem_obj[MAX_FB_BUFFER];  }; @@ -98,10 +95,6 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,  {  	struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); -	/* This fb should have only one gem object. */ -	if (WARN_ON(exynos_fb->buf_cnt != 1)) -		return -EINVAL; -  	return drm_gem_handle_create(file_priv,  			&exynos_fb->exynos_gem_obj[0]->base, handle);  } @@ -122,138 +115,96 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {  	.dirty		= exynos_drm_fb_dirty,  }; -void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, -						unsigned int cnt) -{ -	struct exynos_drm_fb *exynos_fb; - -	exynos_fb = to_exynos_fb(fb); - -	exynos_fb->buf_cnt = cnt; -} - -unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb) -{ -	struct exynos_drm_fb *exynos_fb; - -	exynos_fb = to_exynos_fb(fb); - -	return exynos_fb->buf_cnt; -} -  struct drm_framebuffer *  exynos_drm_framebuffer_init(struct drm_device *dev,  			    struct drm_mode_fb_cmd2 *mode_cmd, -			    struct drm_gem_object *obj) +			    struct exynos_drm_gem_obj **gem_obj, +			    int count)  {  	struct exynos_drm_fb *exynos_fb; -	struct exynos_drm_gem_obj *exynos_gem_obj; +	int i;  	int ret; -	exynos_gem_obj = to_exynos_gem_obj(obj); - -	ret = check_fb_gem_memory_type(dev, exynos_gem_obj); -	if (ret < 0) -		return ERR_PTR(ret); -  	exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);  	if (!exynos_fb)  		return ERR_PTR(-ENOMEM); +	for (i = 0; i < count; i++) { +		ret = check_fb_gem_memory_type(dev, gem_obj[i]); +		if (ret < 0) +			goto err; + +		exynos_fb->exynos_gem_obj[i] = gem_obj[i]; +	} +  	drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); -	exynos_fb->exynos_gem_obj[0] = exynos_gem_obj;  	ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); -	if (ret) { -		kfree(exynos_fb); +	if (ret < 0) {  		DRM_ERROR("failed to initialize framebuffer\n"); -		return ERR_PTR(ret); +		goto err;  	}  	return &exynos_fb->fb; + +err: +	kfree(exynos_fb); +	return ERR_PTR(ret);  }  static struct drm_framebuffer *  exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,  		      struct drm_mode_fb_cmd2 *mode_cmd)  { +	struct exynos_drm_gem_obj *gem_objs[MAX_FB_BUFFER];  	struct drm_gem_object *obj; -	struct exynos_drm_gem_obj *exynos_gem_obj; -	struct exynos_drm_fb *exynos_fb; -	int i, ret; - -	exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); -	if (!exynos_fb) -		return ERR_PTR(-ENOMEM); - -	obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); -	if (!obj) { -		DRM_ERROR("failed to lookup gem object\n"); -		ret = -ENOENT; -		goto err_free; -	} - -	drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); -	exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj); -	exynos_fb->buf_cnt = drm_format_num_planes(mode_cmd->pixel_format); - -	DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); +	struct drm_framebuffer *fb; +	int i; +	int ret; -	for (i = 1; i < exynos_fb->buf_cnt; i++) { +	for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {  		obj = drm_gem_object_lookup(dev, file_priv, -				mode_cmd->handles[i]); +					    mode_cmd->handles[i]);  		if (!obj) {  			DRM_ERROR("failed to lookup gem object\n");  			ret = -ENOENT; -			exynos_fb->buf_cnt = i; -			goto err_unreference; +			goto err;  		} -		exynos_gem_obj = to_exynos_gem_obj(obj); -		exynos_fb->exynos_gem_obj[i] = exynos_gem_obj; - -		ret = check_fb_gem_memory_type(dev, exynos_gem_obj); -		if (ret < 0) -			goto err_unreference; +		gem_objs[i] = to_exynos_gem_obj(obj);  	} -	ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); -	if (ret) { -		DRM_ERROR("failed to init framebuffer.\n"); -		goto err_unreference; +	fb = exynos_drm_framebuffer_init(dev, mode_cmd, gem_objs, i); +	if (IS_ERR(fb)) { +		ret = PTR_ERR(fb); +		goto err;  	} -	return &exynos_fb->fb; +	return fb; -err_unreference: -	for (i = 0; i < exynos_fb->buf_cnt; i++) { -		struct drm_gem_object *obj; +err: +	while (i--) +		drm_gem_object_unreference_unlocked(&gem_objs[i]->base); -		obj = &exynos_fb->exynos_gem_obj[i]->base; -		if (obj) -			drm_gem_object_unreference_unlocked(obj); -	} -err_free: -	kfree(exynos_fb);  	return ERR_PTR(ret);  } -struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, -						int index) +struct exynos_drm_gem_obj *exynos_drm_fb_gem_obj(struct drm_framebuffer *fb, +						 int index)  {  	struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); -	struct exynos_drm_gem_buf *buffer; +	struct exynos_drm_gem_obj *obj;  	if (index >= MAX_FB_BUFFER)  		return NULL; -	buffer = exynos_fb->exynos_gem_obj[index]->buffer; -	if (!buffer) +	obj = exynos_fb->exynos_gem_obj[index]; +	if (!obj)  		return NULL; -	DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr); +	DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)obj->dma_addr); -	return buffer; +	return obj;  }  static void exynos_drm_output_poll_changed(struct drm_device *dev) @@ -267,41 +218,6 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)  		exynos_drm_fbdev_init(dev);  } -static int exynos_atomic_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; - -	/* This is the point of no return */ - -	drm_atomic_helper_swap_state(dev, state); - -	drm_atomic_helper_commit_modeset_disables(dev, state); - -	drm_atomic_helper_commit_modeset_enables(dev, state); - -	/* -	 * Exynos can't update planes with CRTCs and encoders disabled, -	 * its updates routines, specially for FIMD, requires the clocks -	 * to be enabled. So it is necessary to handle the modeset operations -	 * *before* the commit_planes() step, this way it will always -	 * have the relevant clocks enabled to perform the update. -	 */ - -	drm_atomic_helper_commit_planes(dev, state); - -	drm_atomic_helper_cleanup_planes(dev, state); - -	drm_atomic_state_free(state); - -	return 0; -} -  static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {  	.fb_create = exynos_user_fb_create,  	.output_poll_changed = exynos_drm_output_poll_changed, diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h index 517471b37566..85e4445b920e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.h +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -14,22 +14,18 @@  #ifndef _EXYNOS_DRM_FB_H_  #define _EXYNOS_DRM_FB_H +#include "exynos_drm_gem.h" +  struct drm_framebuffer *  exynos_drm_framebuffer_init(struct drm_device *dev,  			    struct drm_mode_fb_cmd2 *mode_cmd, -			    struct drm_gem_object *obj); +			    struct exynos_drm_gem_obj **gem_obj, +			    int count); -/* get memory information of a drm framebuffer */ -struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, +/* get gem object of a drm framebuffer */ +struct exynos_drm_gem_obj *exynos_drm_fb_gem_obj(struct drm_framebuffer *fb,  						 int index);  void exynos_drm_mode_config_init(struct drm_device *dev); -/* set a buffer count to drm framebuffer. */ -void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, -						unsigned int cnt); - -/* get a buffer count to drm framebuffer. */ -unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb); -  #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index e0b085b4bdfa..a221f753ad9c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -21,7 +21,6 @@  #include "exynos_drm_drv.h"  #include "exynos_drm_fb.h"  #include "exynos_drm_fbdev.h" -#include "exynos_drm_gem.h"  #include "exynos_drm_iommu.h"  #define MAX_CONNECTOR		4 @@ -32,7 +31,7 @@  struct exynos_drm_fbdev {  	struct drm_fb_helper		drm_fb_helper; -	struct exynos_drm_gem_obj	*exynos_gem_obj; +	struct exynos_drm_gem_obj	*obj;  };  static int exynos_drm_fb_mmap(struct fb_info *info, @@ -40,8 +39,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info,  {  	struct drm_fb_helper *helper = info->par;  	struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper); -	struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; -	struct exynos_drm_gem_buf *buffer = exynos_gem_obj->buffer; +	struct exynos_drm_gem_obj *obj = exynos_fbd->obj;  	unsigned long vm_size;  	int ret; @@ -49,11 +47,11 @@ static int exynos_drm_fb_mmap(struct fb_info *info,  	vm_size = vma->vm_end - vma->vm_start; -	if (vm_size > buffer->size) +	if (vm_size > obj->size)  		return -EINVAL; -	ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages, -		buffer->dma_addr, buffer->size, &buffer->dma_attrs); +	ret = dma_mmap_attrs(helper->dev->dev, vma, obj->pages, obj->dma_addr, +			     obj->size, &obj->dma_attrs);  	if (ret < 0) {  		DRM_ERROR("failed to mmap.\n");  		return ret; @@ -65,9 +63,9 @@ static int exynos_drm_fb_mmap(struct fb_info *info,  static struct fb_ops exynos_drm_fb_ops = {  	.owner		= THIS_MODULE,  	.fb_mmap        = exynos_drm_fb_mmap, -	.fb_fillrect	= cfb_fillrect, -	.fb_copyarea	= cfb_copyarea, -	.fb_imageblit	= cfb_imageblit, +	.fb_fillrect	= drm_fb_helper_cfb_fillrect, +	.fb_copyarea	= drm_fb_helper_cfb_copyarea, +	.fb_imageblit	= drm_fb_helper_cfb_imageblit,  	.fb_check_var	= drm_fb_helper_check_var,  	.fb_set_par	= drm_fb_helper_set_par,  	.fb_blank	= drm_fb_helper_blank, @@ -76,42 +74,42 @@ static struct fb_ops exynos_drm_fb_ops = {  };  static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, -				     struct drm_fb_helper_surface_size *sizes, -				     struct drm_framebuffer *fb) +				   struct drm_fb_helper_surface_size *sizes, +				   struct exynos_drm_gem_obj *obj)  { -	struct fb_info *fbi = helper->fbdev; -	struct exynos_drm_gem_buf *buffer; +	struct fb_info *fbi; +	struct drm_framebuffer *fb = helper->fb;  	unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);  	unsigned int nr_pages;  	unsigned long offset; +	fbi = drm_fb_helper_alloc_fbi(helper); +	if (IS_ERR(fbi)) { +		DRM_ERROR("failed to allocate fb info.\n"); +		return PTR_ERR(fbi); +	} + +	fbi->par = helper; +	fbi->flags = FBINFO_FLAG_DEFAULT; +	fbi->fbops = &exynos_drm_fb_ops; +  	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);  	drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); -	/* RGB formats use only one buffer */ -	buffer = exynos_drm_fb_buffer(fb, 0); -	if (!buffer) { -		DRM_DEBUG_KMS("buffer is null.\n"); -		return -EFAULT; -	} - -	nr_pages = buffer->size >> PAGE_SHIFT; +	nr_pages = obj->size >> PAGE_SHIFT; -	buffer->kvaddr = (void __iomem *) vmap(buffer->pages, -			nr_pages, VM_MAP, +	obj->kvaddr = (void __iomem *) vmap(obj->pages, nr_pages, VM_MAP,  			pgprot_writecombine(PAGE_KERNEL)); -	if (!buffer->kvaddr) { +	if (!obj->kvaddr) {  		DRM_ERROR("failed to map pages to kernel space.\n"); +		drm_fb_helper_release_fbi(helper);  		return -EIO;  	} -	/* buffer count to framebuffer always is 1 at booting time. */ -	exynos_drm_fb_set_buf_cnt(fb, 1); -  	offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);  	offset += fbi->var.yoffset * fb->pitches[0]; -	fbi->screen_base = buffer->kvaddr + offset; +	fbi->screen_base = obj->kvaddr + offset;  	fbi->screen_size = size;  	fbi->fix.smem_len = size; @@ -122,9 +120,8 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,  				    struct drm_fb_helper_surface_size *sizes)  {  	struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); -	struct exynos_drm_gem_obj *exynos_gem_obj; +	struct exynos_drm_gem_obj *obj;  	struct drm_device *dev = helper->dev; -	struct fb_info *fbi;  	struct drm_mode_fb_cmd2 mode_cmd = { 0 };  	struct platform_device *pdev = dev->platformdev;  	unsigned long size; @@ -142,69 +139,44 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,  	mutex_lock(&dev->struct_mutex); -	fbi = framebuffer_alloc(0, &pdev->dev); -	if (!fbi) { -		DRM_ERROR("failed to allocate fb info.\n"); -		ret = -ENOMEM; -		goto out; -	} -  	size = mode_cmd.pitches[0] * mode_cmd.height; -	exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size); +	obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size);  	/*  	 * If physically contiguous memory allocation fails and if IOMMU is  	 * supported then try to get buffer from non physically contiguous  	 * memory area.  	 */ -	if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) { +	if (IS_ERR(obj) && is_drm_iommu_supported(dev)) {  		dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n"); -		exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG, -							size); +		obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG, size);  	} -	if (IS_ERR(exynos_gem_obj)) { -		ret = PTR_ERR(exynos_gem_obj); -		goto err_release_framebuffer; +	if (IS_ERR(obj)) { +		ret = PTR_ERR(obj); +		goto out;  	} -	exynos_fbdev->exynos_gem_obj = exynos_gem_obj; +	exynos_fbdev->obj = obj; -	helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, -			&exynos_gem_obj->base); +	helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, &obj, 1);  	if (IS_ERR(helper->fb)) {  		DRM_ERROR("failed to create drm framebuffer.\n");  		ret = PTR_ERR(helper->fb);  		goto err_destroy_gem;  	} -	helper->fbdev = fbi; - -	fbi->par = helper; -	fbi->flags = FBINFO_FLAG_DEFAULT; -	fbi->fbops = &exynos_drm_fb_ops; - -	ret = fb_alloc_cmap(&fbi->cmap, 256, 0); -	if (ret) { -		DRM_ERROR("failed to allocate cmap.\n"); -		goto err_destroy_framebuffer; -	} - -	ret = exynos_drm_fbdev_update(helper, sizes, helper->fb); +	ret = exynos_drm_fbdev_update(helper, sizes, obj);  	if (ret < 0) -		goto err_dealloc_cmap; +		goto err_destroy_framebuffer;  	mutex_unlock(&dev->struct_mutex);  	return ret; -err_dealloc_cmap: -	fb_dealloc_cmap(&fbi->cmap);  err_destroy_framebuffer:  	drm_framebuffer_cleanup(helper->fb);  err_destroy_gem: -	exynos_drm_gem_destroy(exynos_gem_obj); -err_release_framebuffer: -	framebuffer_release(fbi); +	exynos_drm_gem_destroy(obj);  /*   * if failed, all resources allocated above would be released by @@ -297,11 +269,11 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,  				      struct drm_fb_helper *fb_helper)  {  	struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper); -	struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; +	struct exynos_drm_gem_obj *obj = exynos_fbd->obj;  	struct drm_framebuffer *fb; -	if (exynos_gem_obj->buffer->kvaddr) -		vunmap(exynos_gem_obj->buffer->kvaddr); +	if (obj->kvaddr) +		vunmap(obj->kvaddr);  	/* release drm framebuffer and real buffer */  	if (fb_helper->fb && fb_helper->fb->funcs) { @@ -312,21 +284,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,  		}  	} -	/* release linux framebuffer */ -	if (fb_helper->fbdev) { -		struct fb_info *info; -		int ret; - -		info = fb_helper->fbdev; -		ret = unregister_framebuffer(info); -		if (ret < 0) -			DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); - -		if (info->cmap.len) -			fb_dealloc_cmap(&info->cmap); - -		framebuffer_release(info); -	} +	drm_fb_helper_unregister_fbi(fb_helper); +	drm_fb_helper_release_fbi(fb_helper);  	drm_fb_helper_fini(fb_helper);  } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 842d6b8dc3c4..2a652359af64 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1745,7 +1745,6 @@ static int fimc_probe(struct platform_device *pdev)  	spin_lock_init(&ctx->lock);  	platform_set_drvdata(pdev, ctx); -	pm_runtime_set_active(dev);  	pm_runtime_enable(dev);  	ret = exynos_drm_ippdrv_register(ippdrv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 794e56c8798e..750a9e6b9e8d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -59,6 +59,7 @@  #define VIDWnALPHA1(win)	(VIDW_ALPHA + 0x04 + (win) * 8)  #define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8) +#define VIDWx_BUF_START_S(win, buf)	(VIDW_BUF_START_S(buf) + (win) * 8)  #define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)  #define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4) @@ -169,7 +170,7 @@ struct fimd_context {  	struct exynos_drm_panel_info panel;  	struct fimd_driver_data *driver_data; -	struct exynos_drm_display *display; +	struct drm_encoder *encoder;  };  static const struct of_device_id fimd_driver_dt_match[] = { @@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = {  };  MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); +static const uint32_t fimd_formats[] = { +	DRM_FORMAT_C8, +	DRM_FORMAT_XRGB1555, +	DRM_FORMAT_RGB565, +	DRM_FORMAT_XRGB8888, +	DRM_FORMAT_ARGB8888, +}; +  static inline struct fimd_driver_data *drm_fimd_get_driver_data(  	struct platform_device *pdev)  { @@ -348,13 +357,6 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)  	pm_runtime_put(ctx->dev);  } -static void fimd_iommu_detach_devices(struct fimd_context *ctx) -{ -	/* detach this sub driver from iommu mapping if supported. */ -	if (is_drm_iommu_supported(ctx->drm_dev)) -		drm_iommu_detach_device(ctx->drm_dev, ctx->dev); -} -  static u32 fimd_calc_clkdiv(struct fimd_context *ctx,  		const struct drm_display_mode *mode)  { @@ -486,9 +488,9 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)  } -static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) +static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, +				struct drm_framebuffer *fb)  { -	struct exynos_drm_plane *plane = &ctx->planes[win];  	unsigned long val;  	val = WINCONx_ENWIN; @@ -498,11 +500,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)  	 * So the request format is ARGB8888 then change it to XRGB8888.  	 */  	if (ctx->driver_data->has_limited_fmt && !win) { -		if (plane->pixel_format == DRM_FORMAT_ARGB8888) -			plane->pixel_format = DRM_FORMAT_XRGB8888; +		if (fb->pixel_format == DRM_FORMAT_ARGB8888) +			fb->pixel_format = DRM_FORMAT_XRGB8888;  	} -	switch (plane->pixel_format) { +	switch (fb->pixel_format) {  	case DRM_FORMAT_C8:  		val |= WINCON0_BPPMODE_8BPP_PALETTE;  		val |= WINCONx_BURSTLEN_8WORD; @@ -538,7 +540,7 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)  		break;  	} -	DRM_DEBUG_KMS("bpp = %d\n", plane->bpp); +	DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);  	/*  	 * In case of exynos, setting dma-burst to 16Word causes permanent @@ -548,7 +550,7 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)  	 * movement causes unstable DMA which results into iommu crash/tear.  	 */ -	if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) { +	if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) {  		val &= ~WINCONx_BURSTLEN_MASK;  		val |= WINCONx_BURSTLEN_4WORD;  	} @@ -598,6 +600,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,  {  	u32 reg, bits, val; +	/* +	 * SHADOWCON/PRTCON register is used for enabling timing. +	 * +	 * for example, once only width value of a register is set, +	 * if the dma is started then fimd hardware could malfunction so +	 * with protect window setting, the register fields with prefix '_F' +	 * wouldn't be updated at vsync also but updated once unprotect window +	 * is set. +	 */ +  	if (ctx->driver_data->has_shadowcon) {  		reg = SHADOWCON;  		bits = SHADOWCON_WINx_PROTECT(win); @@ -614,41 +626,45 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,  	writel(val, ctx->regs + reg);  } -static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) +static void fimd_atomic_begin(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane)  {  	struct fimd_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane; -	dma_addr_t dma_addr; -	unsigned long val, size, offset; -	unsigned int last_x, last_y, buf_offsize, line_size;  	if (ctx->suspended)  		return; -	if (win < 0 || win >= WINDOWS_NR) -		return; +	fimd_shadow_protect_win(ctx, plane->zpos, true); +} -	plane = &ctx->planes[win]; +static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane) +{ +	struct fimd_context *ctx = crtc->ctx;  	if (ctx->suspended)  		return; -	/* -	 * SHADOWCON/PRTCON register is used for enabling timing. -	 * -	 * for example, once only width value of a register is set, -	 * if the dma is started then fimd hardware could malfunction so -	 * with protect window setting, the register fields with prefix '_F' -	 * wouldn't be updated at vsync also but updated once unprotect window -	 * is set. -	 */ +	fimd_shadow_protect_win(ctx, plane->zpos, false); +} -	/* protect windows */ -	fimd_shadow_protect_win(ctx, win, true); +static void fimd_update_plane(struct exynos_drm_crtc *crtc, +			      struct exynos_drm_plane *plane) +{ +	struct fimd_context *ctx = crtc->ctx; +	struct drm_plane_state *state = plane->base.state; +	dma_addr_t dma_addr; +	unsigned long val, size, offset; +	unsigned int last_x, last_y, buf_offsize, line_size; +	unsigned int win = plane->zpos; +	unsigned int bpp = state->fb->bits_per_pixel >> 3; +	unsigned int pitch = state->fb->pitches[0]; +	if (ctx->suspended) +		return; -	offset = plane->src_x * (plane->bpp >> 3); -	offset += plane->src_y * plane->pitch; +	offset = plane->src_x * bpp; +	offset += plane->src_y * pitch;  	/* buffer start address */  	dma_addr = plane->dma_addr[0] + offset; @@ -656,18 +672,18 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));  	/* buffer end address */ -	size = plane->pitch * plane->crtc_height; +	size = pitch * plane->crtc_h;  	val = (unsigned long)(dma_addr + size);  	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));  	DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",  			(unsigned long)dma_addr, val, size);  	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", -			plane->crtc_width, plane->crtc_height); +			plane->crtc_w, plane->crtc_h);  	/* buffer size */ -	buf_offsize = plane->pitch - (plane->crtc_width * (plane->bpp >> 3)); -	line_size = plane->crtc_width * (plane->bpp >> 3); +	buf_offsize = pitch - (plane->crtc_w * bpp); +	line_size = plane->crtc_w * bpp;  	val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |  		VIDW_BUF_SIZE_PAGEWIDTH(line_size) |  		VIDW_BUF_SIZE_OFFSET_E(buf_offsize) | @@ -681,10 +697,10 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  		VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y);  	writel(val, ctx->regs + VIDOSD_A(win)); -	last_x = plane->crtc_x + plane->crtc_width; +	last_x = plane->crtc_x + plane->crtc_w;  	if (last_x)  		last_x--; -	last_y = plane->crtc_y + plane->crtc_height; +	last_y = plane->crtc_y + plane->crtc_h;  	if (last_y)  		last_y--; @@ -701,13 +717,13 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  		u32 offset = VIDOSD_D(win);  		if (win == 0)  			offset = VIDOSD_C(win); -		val = plane->crtc_width * plane->crtc_height; +		val = plane->crtc_w * plane->crtc_h;  		writel(val, ctx->regs + offset);  		DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);  	} -	fimd_win_set_pixfmt(ctx, win); +	fimd_win_set_pixfmt(ctx, win, state->fb);  	/* hardware window 0 doesn't support color key. */  	if (win != 0) @@ -718,36 +734,23 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)  	if (ctx->driver_data->has_shadowcon)  		fimd_enable_shadow_channel_path(ctx, win, true); -	/* Enable DMA channel and unprotect windows */ -	fimd_shadow_protect_win(ctx, win, false); -  	if (ctx->i80_if)  		atomic_set(&ctx->win_updated, 1);  } -static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win) +static void fimd_disable_plane(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane)  {  	struct fimd_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane; - -	if (win < 0 || win >= WINDOWS_NR) -		return; - -	plane = &ctx->planes[win]; +	unsigned int win = plane->zpos;  	if (ctx->suspended)  		return; -	/* protect windows */ -	fimd_shadow_protect_win(ctx, win, true); -  	fimd_enable_video_output(ctx, win, false);  	if (ctx->driver_data->has_shadowcon)  		fimd_enable_shadow_channel_path(ctx, win, false); - -	/* unprotect windows */ -	fimd_shadow_protect_win(ctx, win, false);  }  static void fimd_enable(struct exynos_drm_crtc *crtc) @@ -795,7 +798,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)  	 * a destroyed buffer later.  	 */  	for (i = 0; i < WINDOWS_NR; i++) -		fimd_win_disable(crtc, i); +		fimd_disable_plane(crtc, &ctx->planes[i]);  	fimd_enable_vblank(crtc);  	fimd_wait_for_vblank(crtc); @@ -862,7 +865,7 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)  	}  	if (test_bit(0, &ctx->irq_flags)) -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); +		drm_crtc_handle_vblank(&ctx->crtc->base);  }  static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable) @@ -890,17 +893,19 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {  	.enable_vblank = fimd_enable_vblank,  	.disable_vblank = fimd_disable_vblank,  	.wait_for_vblank = fimd_wait_for_vblank, -	.win_commit = fimd_win_commit, -	.win_disable = fimd_win_disable, +	.atomic_begin = fimd_atomic_begin, +	.update_plane = fimd_update_plane, +	.disable_plane = fimd_disable_plane, +	.atomic_flush = fimd_atomic_flush,  	.te_handler = fimd_te_handler,  	.clock_enable = fimd_dp_clock_enable, -	.clear_channels = fimd_clear_channels,  };  static irqreturn_t fimd_irq_handler(int irq, void *dev_id)  {  	struct fimd_context *ctx = (struct fimd_context *)dev_id; -	u32 val, clear_bit; +	u32 val, clear_bit, start, start_s; +	int win;  	val = readl(ctx->regs + VIDINTCON1); @@ -912,15 +917,25 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)  	if (ctx->pipe < 0 || !ctx->drm_dev)  		goto out; -	if (ctx->i80_if) { -		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); +	if (!ctx->i80_if) +		drm_crtc_handle_vblank(&ctx->crtc->base); + +	for (win = 0 ; win < WINDOWS_NR ; win++) { +		struct exynos_drm_plane *plane = &ctx->planes[win]; + +		if (!plane->pending_fb) +			continue; +		start = readl(ctx->regs + VIDWx_BUF_START(win, 0)); +		start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0)); +		if (start == start_s) +			exynos_drm_crtc_finish_update(ctx->crtc, plane); +	} + +	if (ctx->i80_if) {  		/* Exits triggering mode */  		atomic_set(&ctx->triggering, 0);  	} else { -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); -		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); -  		/* set wait vsync event to zero and wake up queue. */  		if (atomic_read(&ctx->wait_vsync_event)) {  			atomic_set(&ctx->wait_vsync_event, 0); @@ -949,7 +964,8 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)  		type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :  						DRM_PLANE_TYPE_OVERLAY;  		ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], -					1 << ctx->pipe, type, zpos); +					1 << ctx->pipe, type, fimd_formats, +					ARRAY_SIZE(fimd_formats), zpos);  		if (ret)  			return ret;  	} @@ -961,10 +977,13 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)  	if (IS_ERR(ctx->crtc))  		return PTR_ERR(ctx->crtc); -	if (ctx->display) -		exynos_drm_create_enc_conn(drm_dev, ctx->display); +	if (ctx->encoder) +		exynos_dpi_bind(drm_dev, ctx->encoder); + +	if (is_drm_iommu_supported(drm_dev)) +		fimd_clear_channels(ctx->crtc); -	ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev); +	ret = drm_iommu_attach_device(drm_dev, dev);  	if (ret)  		priv->pipe--; @@ -978,10 +997,10 @@ static void fimd_unbind(struct device *dev, struct device *master,  	fimd_disable(ctx->crtc); -	fimd_iommu_detach_devices(ctx); +	drm_iommu_detach_device(ctx->drm_dev, ctx->dev); -	if (ctx->display) -		exynos_dpi_remove(ctx->display); +	if (ctx->encoder) +		exynos_dpi_remove(ctx->encoder);  }  static const struct component_ops fimd_component_ops = { @@ -1088,10 +1107,9 @@ static int fimd_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, ctx); -	ctx->display = exynos_dpi_probe(dev); -	if (IS_ERR(ctx->display)) { -		return PTR_ERR(ctx->display); -	} +	ctx->encoder = exynos_dpi_probe(dev); +	if (IS_ERR(ctx->encoder)) +		return PTR_ERR(ctx->encoder);  	pm_runtime_enable(dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..3734c34aed16 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -48,11 +48,13 @@  /* registers for base address */  #define G2D_SRC_BASE_ADDR		0x0304 +#define G2D_SRC_STRIDE_REG		0x0308  #define G2D_SRC_COLOR_MODE		0x030C  #define G2D_SRC_LEFT_TOP		0x0310  #define G2D_SRC_RIGHT_BOTTOM		0x0314  #define G2D_SRC_PLANE2_BASE_ADDR	0x0318  #define G2D_DST_BASE_ADDR		0x0404 +#define G2D_DST_STRIDE_REG		0x0408  #define G2D_DST_COLOR_MODE		0x040C  #define G2D_DST_LEFT_TOP		0x0410  #define G2D_DST_RIGHT_BOTTOM		0x0414 @@ -148,6 +150,7 @@ struct g2d_cmdlist {   * A structure of buffer description   *   * @format: color format + * @stride: buffer stride/pitch in bytes   * @left_x: the x coordinates of left top corner   * @top_y: the y coordinates of left top corner   * @right_x: the x coordinates of right bottom corner @@ -156,6 +159,7 @@ struct g2d_cmdlist {   */  struct g2d_buf_desc {  	unsigned int	format; +	unsigned int	stride;  	unsigned int	left_x;  	unsigned int	top_y;  	unsigned int	right_x; @@ -190,10 +194,8 @@ struct g2d_cmdlist_userptr {  	dma_addr_t		dma_addr;  	unsigned long		userptr;  	unsigned long		size; -	struct page		**pages; -	unsigned int		npages; +	struct frame_vector	*vec;  	struct sg_table		*sgt; -	struct vm_area_struct	*vma;  	atomic_t		refcount;  	bool			in_pool;  	bool			out_of_list; @@ -363,6 +365,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,  {  	struct g2d_cmdlist_userptr *g2d_userptr =  					(struct g2d_cmdlist_userptr *)obj; +	struct page **pages;  	if (!obj)  		return; @@ -382,19 +385,21 @@ out:  	exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,  					DMA_BIDIRECTIONAL); -	exynos_gem_put_pages_to_userptr(g2d_userptr->pages, -					g2d_userptr->npages, -					g2d_userptr->vma); +	pages = frame_vector_pages(g2d_userptr->vec); +	if (!IS_ERR(pages)) { +		int i; -	exynos_gem_put_vma(g2d_userptr->vma); +		for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) +			set_page_dirty_lock(pages[i]); +	} +	put_vaddr_frames(g2d_userptr->vec); +	frame_vector_destroy(g2d_userptr->vec);  	if (!g2d_userptr->out_of_list)  		list_del_init(&g2d_userptr->list);  	sg_free_table(g2d_userptr->sgt);  	kfree(g2d_userptr->sgt); - -	drm_free_large(g2d_userptr->pages);  	kfree(g2d_userptr);  } @@ -408,9 +413,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  	struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;  	struct g2d_cmdlist_userptr *g2d_userptr;  	struct g2d_data *g2d; -	struct page **pages;  	struct sg_table	*sgt; -	struct vm_area_struct *vma;  	unsigned long start, end;  	unsigned int npages, offset;  	int ret; @@ -456,65 +459,40 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  		return ERR_PTR(-ENOMEM);  	atomic_set(&g2d_userptr->refcount, 1); +	g2d_userptr->size = size;  	start = userptr & PAGE_MASK;  	offset = userptr & ~PAGE_MASK;  	end = PAGE_ALIGN(userptr + size);  	npages = (end - start) >> PAGE_SHIFT; -	g2d_userptr->npages = npages; - -	pages = drm_calloc_large(npages, sizeof(struct page *)); -	if (!pages) { -		DRM_ERROR("failed to allocate pages.\n"); +	g2d_userptr->vec = frame_vector_create(npages); +	if (!g2d_userptr->vec) {  		ret = -ENOMEM;  		goto err_free;  	} -	down_read(¤t->mm->mmap_sem); -	vma = find_vma(current->mm, userptr); -	if (!vma) { -		up_read(¤t->mm->mmap_sem); -		DRM_ERROR("failed to get vm region.\n"); +	ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec); +	if (ret != npages) { +		DRM_ERROR("failed to get user pages from userptr.\n"); +		if (ret < 0) +			goto err_destroy_framevec;  		ret = -EFAULT; -		goto err_free_pages; +		goto err_put_framevec;  	} - -	if (vma->vm_end < userptr + size) { -		up_read(¤t->mm->mmap_sem); -		DRM_ERROR("vma is too small.\n"); +	if (frame_vector_to_pages(g2d_userptr->vec) < 0) {  		ret = -EFAULT; -		goto err_free_pages; -	} - -	g2d_userptr->vma = exynos_gem_get_vma(vma); -	if (!g2d_userptr->vma) { -		up_read(¤t->mm->mmap_sem); -		DRM_ERROR("failed to copy vma.\n"); -		ret = -ENOMEM; -		goto err_free_pages; -	} - -	g2d_userptr->size = size; - -	ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, -						npages, pages, vma); -	if (ret < 0) { -		up_read(¤t->mm->mmap_sem); -		DRM_ERROR("failed to get user pages from userptr.\n"); -		goto err_put_vma; +		goto err_put_framevec;  	} -	up_read(¤t->mm->mmap_sem); -	g2d_userptr->pages = pages; -  	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);  	if (!sgt) {  		ret = -ENOMEM; -		goto err_free_userptr; +		goto err_put_framevec;  	} -	ret = sg_alloc_table_from_pages(sgt, pages, npages, offset, -					size, GFP_KERNEL); +	ret = sg_alloc_table_from_pages(sgt, +					frame_vector_pages(g2d_userptr->vec), +					npages, offset, size, GFP_KERNEL);  	if (ret < 0) {  		DRM_ERROR("failed to get sgt from pages.\n");  		goto err_free_sgt; @@ -549,16 +527,11 @@ err_sg_free_table:  err_free_sgt:  	kfree(sgt); -err_free_userptr: -	exynos_gem_put_pages_to_userptr(g2d_userptr->pages, -					g2d_userptr->npages, -					g2d_userptr->vma); - -err_put_vma: -	exynos_gem_put_vma(g2d_userptr->vma); +err_put_framevec: +	put_vaddr_frames(g2d_userptr->vec); -err_free_pages: -	drm_free_large(pages); +err_destroy_framevec: +	frame_vector_destroy(g2d_userptr->vec);  err_free:  	kfree(g2d_userptr); @@ -589,6 +562,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)  	switch (reg_offset) {  	case G2D_SRC_BASE_ADDR: +	case G2D_SRC_STRIDE_REG:  	case G2D_SRC_COLOR_MODE:  	case G2D_SRC_LEFT_TOP:  	case G2D_SRC_RIGHT_BOTTOM: @@ -598,6 +572,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)  		reg_type = REG_TYPE_SRC_PLANE2;  		break;  	case G2D_DST_BASE_ADDR: +	case G2D_DST_STRIDE_REG:  	case G2D_DST_COLOR_MODE:  	case G2D_DST_LEFT_TOP:  	case G2D_DST_RIGHT_BOTTOM: @@ -652,8 +627,8 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,  						enum g2d_reg_type reg_type,  						unsigned long size)  { -	unsigned int width, height; -	unsigned long area; +	int width, height; +	unsigned long bpp, last_pos;  	/*  	 * check source and destination buffers only. @@ -662,22 +637,37 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,  	if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST)  		return true; -	width = buf_desc->right_x - buf_desc->left_x; +	/* This check also makes sure that right_x > left_x. */ +	width = (int)buf_desc->right_x - (int)buf_desc->left_x;  	if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { -		DRM_ERROR("width[%u] is out of range!\n", width); +		DRM_ERROR("width[%d] is out of range!\n", width);  		return false;  	} -	height = buf_desc->bottom_y - buf_desc->top_y; +	/* This check also makes sure that bottom_y > top_y. */ +	height = (int)buf_desc->bottom_y - (int)buf_desc->top_y;  	if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { -		DRM_ERROR("height[%u] is out of range!\n", height); +		DRM_ERROR("height[%d] is out of range!\n", height);  		return false;  	} -	area = (unsigned long)width * (unsigned long)height * -					g2d_get_buf_bpp(buf_desc->format); -	if (area > size) { -		DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); +	bpp = g2d_get_buf_bpp(buf_desc->format); + +	/* Compute the position of the last byte that the engine accesses. */ +	last_pos = ((unsigned long)buf_desc->bottom_y - 1) * +		(unsigned long)buf_desc->stride + +		(unsigned long)buf_desc->right_x * bpp - 1; + +	/* +	 * Since right_x > left_x and bottom_y > top_y we already know +	 * that the first_pos < last_pos (first_pos being the position +	 * of the first byte the engine accesses), it just remains to +	 * check if last_pos is smaller then the buffer size. +	 */ + +	if (last_pos >= size) { +		DRM_ERROR("last engine access position [%lu] " +			"is out of range [%lu]!\n", last_pos, size);  		return false;  	} @@ -973,8 +963,6 @@ static int g2d_check_reg_offset(struct device *dev,  				goto err;  			reg_type = g2d_get_reg_type(reg_offset); -			if (reg_type == REG_TYPE_NONE) -				goto err;  			/* check userptr buffer type. */  			if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { @@ -983,14 +971,22 @@ static int g2d_check_reg_offset(struct device *dev,  			} else  				buf_info->types[reg_type] = BUF_TYPE_GEM;  			break; +		case G2D_SRC_STRIDE_REG: +		case G2D_DST_STRIDE_REG: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); + +			buf_desc = &buf_info->descs[reg_type]; +			buf_desc->stride = cmdlist->data[index + 1]; +			break;  		case G2D_SRC_COLOR_MODE:  		case G2D_DST_COLOR_MODE:  			if (for_addr)  				goto err;  			reg_type = g2d_get_reg_type(reg_offset); -			if (reg_type == REG_TYPE_NONE) -				goto err;  			buf_desc = &buf_info->descs[reg_type];  			value = cmdlist->data[index + 1]; @@ -1003,8 +999,6 @@ static int g2d_check_reg_offset(struct device *dev,  				goto err;  			reg_type = g2d_get_reg_type(reg_offset); -			if (reg_type == REG_TYPE_NONE) -				goto err;  			buf_desc = &buf_info->descs[reg_type];  			value = cmdlist->data[index + 1]; @@ -1018,8 +1012,6 @@ static int g2d_check_reg_offset(struct device *dev,  				goto err;  			reg_type = g2d_get_reg_type(reg_offset); -			if (reg_type == REG_TYPE_NONE) -				goto err;  			buf_desc = &buf_info->descs[reg_type];  			value = cmdlist->data[index + 1]; @@ -1319,9 +1311,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)  		return ret;  	} -	if (!is_drm_iommu_supported(drm_dev)) -		return 0; -  	ret = drm_iommu_attach_device(drm_dev, dev);  	if (ret < 0) {  		dev_err(dev, "failed to enable iommu.\n"); @@ -1334,9 +1323,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)  static void g2d_subdrv_remove(struct drm_device *drm_dev, struct device *dev)  { -	if (!is_drm_iommu_supported(drm_dev)) -		return; -  	drm_iommu_detach_device(drm_dev, dev);  } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 0d5b9698d384..f12fbc36b120 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -13,98 +13,112 @@  #include <drm/drm_vma_manager.h>  #include <linux/shmem_fs.h> +#include <linux/dma-buf.h>  #include <drm/exynos_drm.h>  #include "exynos_drm_drv.h"  #include "exynos_drm_gem.h" -#include "exynos_drm_buf.h"  #include "exynos_drm_iommu.h" -static unsigned int convert_to_vm_err_msg(int msg) +static int exynos_drm_alloc_buf(struct exynos_drm_gem_obj *obj)  { -	unsigned int out_msg; +	struct drm_device *dev = obj->base.dev; +	enum dma_attr attr; +	unsigned int nr_pages; -	switch (msg) { -	case 0: -	case -ERESTARTSYS: -	case -EINTR: -		out_msg = VM_FAULT_NOPAGE; -		break; +	if (obj->dma_addr) { +		DRM_DEBUG_KMS("already allocated.\n"); +		return 0; +	} -	case -ENOMEM: -		out_msg = VM_FAULT_OOM; -		break; +	init_dma_attrs(&obj->dma_attrs); -	default: -		out_msg = VM_FAULT_SIGBUS; -		break; -	} +	/* +	 * if EXYNOS_BO_CONTIG, fully physically contiguous memory +	 * region will be allocated else physically contiguous +	 * as possible. +	 */ +	if (!(obj->flags & EXYNOS_BO_NONCONTIG)) +		dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &obj->dma_attrs); -	return out_msg; -} +	/* +	 * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping +	 * else cachable mapping. +	 */ +	if (obj->flags & EXYNOS_BO_WC || !(obj->flags & EXYNOS_BO_CACHABLE)) +		attr = DMA_ATTR_WRITE_COMBINE; +	else +		attr = DMA_ATTR_NON_CONSISTENT; -static int check_gem_flags(unsigned int flags) -{ -	if (flags & ~(EXYNOS_BO_MASK)) { -		DRM_ERROR("invalid flags.\n"); -		return -EINVAL; -	} +	dma_set_attr(attr, &obj->dma_attrs); +	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &obj->dma_attrs); -	return 0; -} +	nr_pages = obj->size >> PAGE_SHIFT; -static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj, -					struct vm_area_struct *vma) -{ -	DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags); +	if (!is_drm_iommu_supported(dev)) { +		dma_addr_t start_addr; +		unsigned int i = 0; -	/* non-cachable as default. */ -	if (obj->flags & EXYNOS_BO_CACHABLE) -		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); -	else if (obj->flags & EXYNOS_BO_WC) -		vma->vm_page_prot = -			pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); -	else -		vma->vm_page_prot = -			pgprot_noncached(vm_get_page_prot(vma->vm_flags)); -} +		obj->pages = drm_calloc_large(nr_pages, sizeof(struct page *)); +		if (!obj->pages) { +			DRM_ERROR("failed to allocate pages.\n"); +			return -ENOMEM; +		} -static unsigned long roundup_gem_size(unsigned long size, unsigned int flags) -{ -	/* TODO */ +		obj->cookie = dma_alloc_attrs(dev->dev, +					obj->size, +					&obj->dma_addr, GFP_KERNEL, +					&obj->dma_attrs); +		if (!obj->cookie) { +			DRM_ERROR("failed to allocate buffer.\n"); +			drm_free_large(obj->pages); +			return -ENOMEM; +		} -	return roundup(size, PAGE_SIZE); +		start_addr = obj->dma_addr; +		while (i < nr_pages) { +			obj->pages[i] = phys_to_page(start_addr); +			start_addr += PAGE_SIZE; +			i++; +		} +	} else { +		obj->pages = dma_alloc_attrs(dev->dev, obj->size, +					&obj->dma_addr, GFP_KERNEL, +					&obj->dma_attrs); +		if (!obj->pages) { +			DRM_ERROR("failed to allocate buffer.\n"); +			return -ENOMEM; +		} +	} + +	DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", +			(unsigned long)obj->dma_addr, +			obj->size); + +	return 0;  } -static int exynos_drm_gem_map_buf(struct drm_gem_object *obj, -					struct vm_area_struct *vma, -					unsigned long f_vaddr, -					pgoff_t page_offset) +static void exynos_drm_free_buf(struct exynos_drm_gem_obj *obj)  { -	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); -	struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; -	struct scatterlist *sgl; -	unsigned long pfn; -	int i; +	struct drm_device *dev = obj->base.dev; -	if (!buf->sgt) -		return -EINTR; - -	if (page_offset >= (buf->size >> PAGE_SHIFT)) { -		DRM_ERROR("invalid page offset\n"); -		return -EINVAL; +	if (!obj->dma_addr) { +		DRM_DEBUG_KMS("dma_addr is invalid.\n"); +		return;  	} -	sgl = buf->sgt->sgl; -	for_each_sg(buf->sgt->sgl, sgl, buf->sgt->nents, i) { -		if (page_offset < (sgl->length >> PAGE_SHIFT)) -			break; -		page_offset -=	(sgl->length >> PAGE_SHIFT); -	} +	DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", +			(unsigned long)obj->dma_addr, obj->size); -	pfn = __phys_to_pfn(sg_phys(sgl)) + page_offset; +	if (!is_drm_iommu_supported(dev)) { +		dma_free_attrs(dev->dev, obj->size, obj->cookie, +				(dma_addr_t)obj->dma_addr, &obj->dma_attrs); +		drm_free_large(obj->pages); +	} else +		dma_free_attrs(dev->dev, obj->size, obj->pages, +				(dma_addr_t)obj->dma_addr, &obj->dma_attrs); -	return vm_insert_mixed(vma, f_vaddr, pfn); +	obj->dma_addr = (dma_addr_t)NULL;  }  static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, @@ -131,11 +145,7 @@ static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,  void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)  { -	struct drm_gem_object *obj; -	struct exynos_drm_gem_buf *buf; - -	obj = &exynos_gem_obj->base; -	buf = exynos_gem_obj->buffer; +	struct drm_gem_object *obj = &exynos_gem_obj->base;  	DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count); @@ -148,12 +158,9 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)  	if (obj->import_attach)  		goto out; -	exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf); +	exynos_drm_free_buf(exynos_gem_obj);  out: -	exynos_drm_fini_buf(obj->dev, buf); -	exynos_gem_obj->buffer = NULL; -  	drm_gem_free_mmap_offset(obj);  	/* release file pointer to gem object. */ @@ -180,7 +187,7 @@ unsigned long exynos_drm_gem_get_size(struct drm_device *dev,  	drm_gem_object_unreference_unlocked(obj); -	return exynos_gem_obj->buffer->size; +	return exynos_gem_obj->size;  } @@ -193,7 +200,7 @@ struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,  	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);  	if (!exynos_gem_obj) -		return NULL; +		return ERR_PTR(-ENOMEM);  	exynos_gem_obj->size = size;  	obj = &exynos_gem_obj->base; @@ -202,7 +209,7 @@ struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,  	if (ret < 0) {  		DRM_ERROR("failed to initialize gem object\n");  		kfree(exynos_gem_obj); -		return NULL; +		return ERR_PTR(ret);  	}  	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); @@ -215,47 +222,35 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,  						unsigned long size)  {  	struct exynos_drm_gem_obj *exynos_gem_obj; -	struct exynos_drm_gem_buf *buf;  	int ret; +	if (flags & ~(EXYNOS_BO_MASK)) { +		DRM_ERROR("invalid flags.\n"); +		return ERR_PTR(-EINVAL); +	} +  	if (!size) {  		DRM_ERROR("invalid size.\n");  		return ERR_PTR(-EINVAL);  	} -	size = roundup_gem_size(size, flags); - -	ret = check_gem_flags(flags); -	if (ret) -		return ERR_PTR(ret); - -	buf = exynos_drm_init_buf(dev, size); -	if (!buf) -		return ERR_PTR(-ENOMEM); +	size = roundup(size, PAGE_SIZE);  	exynos_gem_obj = exynos_drm_gem_init(dev, size); -	if (!exynos_gem_obj) { -		ret = -ENOMEM; -		goto err_fini_buf; -	} - -	exynos_gem_obj->buffer = buf; +	if (IS_ERR(exynos_gem_obj)) +		return exynos_gem_obj;  	/* set memory type and cache attribute from user side. */  	exynos_gem_obj->flags = flags; -	ret = exynos_drm_alloc_buf(dev, buf, flags); -	if (ret < 0) -		goto err_gem_fini; +	ret = exynos_drm_alloc_buf(exynos_gem_obj); +	if (ret < 0) { +		drm_gem_object_release(&exynos_gem_obj->base); +		kfree(exynos_gem_obj); +		return ERR_PTR(ret); +	}  	return exynos_gem_obj; - -err_gem_fini: -	drm_gem_object_release(&exynos_gem_obj->base); -	kfree(exynos_gem_obj); -err_fini_buf: -	exynos_drm_fini_buf(dev, buf); -	return ERR_PTR(ret);  }  int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, @@ -294,7 +289,7 @@ dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,  	exynos_gem_obj = to_exynos_gem_obj(obj); -	return &exynos_gem_obj->buffer->dma_addr; +	return &exynos_gem_obj->dma_addr;  }  void exynos_drm_gem_put_dma_addr(struct drm_device *dev, @@ -322,7 +317,6 @@ int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,  				      struct vm_area_struct *vma)  {  	struct drm_device *drm_dev = exynos_gem_obj->base.dev; -	struct exynos_drm_gem_buf *buffer;  	unsigned long vm_size;  	int ret; @@ -331,19 +325,13 @@ int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,  	vm_size = vma->vm_end - vma->vm_start; -	/* -	 * a buffer contains information to physically continuous memory -	 * allocated by user request or at framebuffer creation. -	 */ -	buffer = exynos_gem_obj->buffer; -  	/* check if user-requested size is valid. */ -	if (vm_size > buffer->size) +	if (vm_size > exynos_gem_obj->size)  		return -EINVAL; -	ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages, -				buffer->dma_addr, buffer->size, -				&buffer->dma_attrs); +	ret = dma_mmap_attrs(drm_dev->dev, vma, exynos_gem_obj->pages, +				exynos_gem_obj->dma_addr, exynos_gem_obj->size, +				&exynos_gem_obj->dma_attrs);  	if (ret < 0) {  		DRM_ERROR("failed to mmap.\n");  		return ret; @@ -378,103 +366,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,  	return 0;  } -struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma) -{ -	struct vm_area_struct *vma_copy; - -	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); -	if (!vma_copy) -		return NULL; - -	if (vma->vm_ops && vma->vm_ops->open) -		vma->vm_ops->open(vma); - -	if (vma->vm_file) -		get_file(vma->vm_file); - -	memcpy(vma_copy, vma, sizeof(*vma)); - -	vma_copy->vm_mm = NULL; -	vma_copy->vm_next = NULL; -	vma_copy->vm_prev = NULL; - -	return vma_copy; -} - -void exynos_gem_put_vma(struct vm_area_struct *vma) -{ -	if (!vma) -		return; - -	if (vma->vm_ops && vma->vm_ops->close) -		vma->vm_ops->close(vma); - -	if (vma->vm_file) -		fput(vma->vm_file); - -	kfree(vma); -} - -int exynos_gem_get_pages_from_userptr(unsigned long start, -						unsigned int npages, -						struct page **pages, -						struct vm_area_struct *vma) -{ -	int get_npages; - -	/* the memory region mmaped with VM_PFNMAP. */ -	if (vma_is_io(vma)) { -		unsigned int i; - -		for (i = 0; i < npages; ++i, start += PAGE_SIZE) { -			unsigned long pfn; -			int ret = follow_pfn(vma, start, &pfn); -			if (ret) -				return ret; - -			pages[i] = pfn_to_page(pfn); -		} - -		if (i != npages) { -			DRM_ERROR("failed to get user_pages.\n"); -			return -EINVAL; -		} - -		return 0; -	} - -	get_npages = get_user_pages(current, current->mm, start, -					npages, 1, 1, pages, NULL); -	get_npages = max(get_npages, 0); -	if (get_npages != npages) { -		DRM_ERROR("failed to get user_pages.\n"); -		while (get_npages) -			put_page(pages[--get_npages]); -		return -EFAULT; -	} - -	return 0; -} - -void exynos_gem_put_pages_to_userptr(struct page **pages, -					unsigned int npages, -					struct vm_area_struct *vma) -{ -	if (!vma_is_io(vma)) { -		unsigned int i; - -		for (i = 0; i < npages; i++) { -			set_page_dirty_lock(pages[i]); - -			/* -			 * undo the reference we took when populating -			 * the table. -			 */ -			put_page(pages[i]); -		} -	} -} -  int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,  				struct sg_table *sgt,  				enum dma_data_direction dir) @@ -503,15 +394,6 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,  void exynos_drm_gem_free_object(struct drm_gem_object *obj)  { -	struct exynos_drm_gem_obj *exynos_gem_obj; -	struct exynos_drm_gem_buf *buf; - -	exynos_gem_obj = to_exynos_gem_obj(obj); -	buf = exynos_gem_obj->buffer; - -	if (obj->import_attach) -		drm_prime_gem_destroy(obj, buf->sgt); -  	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));  } @@ -595,24 +477,34 @@ unlock:  int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  {  	struct drm_gem_object *obj = vma->vm_private_data; -	struct drm_device *dev = obj->dev; -	unsigned long f_vaddr; +	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); +	unsigned long pfn;  	pgoff_t page_offset;  	int ret;  	page_offset = ((unsigned long)vmf->virtual_address -  			vma->vm_start) >> PAGE_SHIFT; -	f_vaddr = (unsigned long)vmf->virtual_address; - -	mutex_lock(&dev->struct_mutex); -	ret = exynos_drm_gem_map_buf(obj, vma, f_vaddr, page_offset); -	if (ret < 0) -		DRM_ERROR("failed to map a buffer with user.\n"); +	if (page_offset >= (exynos_gem_obj->size >> PAGE_SHIFT)) { +		DRM_ERROR("invalid page offset\n"); +		ret = -EINVAL; +		goto out; +	} -	mutex_unlock(&dev->struct_mutex); +	pfn = page_to_pfn(exynos_gem_obj->pages[page_offset]); +	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); -	return convert_to_vm_err_msg(ret); +out: +	switch (ret) { +	case 0: +	case -ERESTARTSYS: +	case -EINTR: +		return VM_FAULT_NOPAGE; +	case -ENOMEM: +		return VM_FAULT_OOM; +	default: +		return VM_FAULT_SIGBUS; +	}  }  int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) @@ -631,11 +523,17 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)  	obj = vma->vm_private_data;  	exynos_gem_obj = to_exynos_gem_obj(obj); -	ret = check_gem_flags(exynos_gem_obj->flags); -	if (ret) -		goto err_close_vm; +	DRM_DEBUG_KMS("flags = 0x%x\n", exynos_gem_obj->flags); -	update_vm_cache_attr(exynos_gem_obj, vma); +	/* non-cachable as default. */ +	if (exynos_gem_obj->flags & EXYNOS_BO_CACHABLE) +		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); +	else if (exynos_gem_obj->flags & EXYNOS_BO_WC) +		vma->vm_page_prot = +			pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); +	else +		vma->vm_page_prot = +			pgprot_noncached(vm_get_page_prot(vma->vm_flags));  	ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);  	if (ret) @@ -649,3 +547,76 @@ err_close_vm:  	return ret;  } + +/* low-level interface prime helpers */ +struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ +	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); +	int npages; + +	npages = exynos_gem_obj->size >> PAGE_SHIFT; + +	return drm_prime_pages_to_sg(exynos_gem_obj->pages, npages); +} + +struct drm_gem_object * +exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, +				     struct dma_buf_attachment *attach, +				     struct sg_table *sgt) +{ +	struct exynos_drm_gem_obj *exynos_gem_obj; +	int npages; +	int ret; + +	exynos_gem_obj = exynos_drm_gem_init(dev, attach->dmabuf->size); +	if (IS_ERR(exynos_gem_obj)) { +		ret = PTR_ERR(exynos_gem_obj); +		return ERR_PTR(ret); +	} + +	exynos_gem_obj->dma_addr = sg_dma_address(sgt->sgl); + +	npages = exynos_gem_obj->size >> PAGE_SHIFT; +	exynos_gem_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); +	if (!exynos_gem_obj->pages) { +		ret = -ENOMEM; +		goto err; +	} + +	ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem_obj->pages, NULL, +			npages); +	if (ret < 0) +		goto err_free_large; + +	if (sgt->nents == 1) { +		/* always physically continuous memory if sgt->nents is 1. */ +		exynos_gem_obj->flags |= EXYNOS_BO_CONTIG; +	} else { +		/* +		 * this case could be CONTIG or NONCONTIG type but for now +		 * sets NONCONTIG. +		 * TODO. we have to find a way that exporter can notify +		 * the type of its own buffer to importer. +		 */ +		exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG; +	} + +	return &exynos_gem_obj->base; + +err_free_large: +	drm_free_large(exynos_gem_obj->pages); +err: +	drm_gem_object_release(&exynos_gem_obj->base); +	kfree(exynos_gem_obj); +	return ERR_PTR(ret); +} + +void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj) +{ +	return NULL; +} + +void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ +	/* Nothing to do */ +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 6f42e2248288..cd62f8410d1e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -20,35 +20,6 @@  #define IS_NONCONTIG_BUFFER(f)		(f & EXYNOS_BO_NONCONTIG)  /* - * exynos drm gem buffer structure. - * - * @cookie: cookie returned by dma_alloc_attrs - * @kvaddr: kernel virtual address to allocated memory region. - * *userptr: user space address. - * @dma_addr: bus address(accessed by dma) to allocated memory region. - *	- this address could be physical address without IOMMU and - *	device address with IOMMU. - * @write: whether pages will be written to by the caller. - * @pages: Array of backing pages. - * @sgt: sg table to transfer page data. - * @size: size of allocated memory region. - * @pfnmap: indicate whether memory region from userptr is mmaped with - *	VM_PFNMAP or not. - */ -struct exynos_drm_gem_buf { -	void 			*cookie; -	void __iomem		*kvaddr; -	unsigned long		userptr; -	dma_addr_t		dma_addr; -	struct dma_attrs	dma_attrs; -	unsigned int		write; -	struct page		**pages; -	struct sg_table		*sgt; -	unsigned long		size; -	bool			pfnmap; -}; - -/*   * exynos drm buffer structure.   *   * @base: a gem object. @@ -59,18 +30,28 @@ struct exynos_drm_gem_buf {   *	by user request or at framebuffer creation.   *	continuous memory region allocated by user request   *	or at framebuffer creation. + * @flags: indicate memory type to allocated buffer and cache attruibute.   * @size: size requested from user, in bytes and this size is aligned   *	in page unit. - * @flags: indicate memory type to allocated buffer and cache attruibute. + * @cookie: cookie returned by dma_alloc_attrs + * @kvaddr: kernel virtual address to allocated memory region. + * @dma_addr: bus address(accessed by dma) to allocated memory region. + *	- this address could be physical address without IOMMU and + *	device address with IOMMU. + * @pages: Array of backing pages.   *   * P.S. this object would be transferred to user as kms_bo.handle so   *	user can access the buffer through kms_bo.handle.   */  struct exynos_drm_gem_obj { -	struct drm_gem_object		base; -	struct exynos_drm_gem_buf	*buffer; -	unsigned long			size; -	unsigned int			flags; +	struct drm_gem_object	base; +	unsigned int		flags; +	unsigned long		size; +	void			*cookie; +	void __iomem		*kvaddr; +	dma_addr_t		dma_addr; +	struct dma_attrs	dma_attrs; +	struct page		**pages;  };  struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); @@ -177,4 +158,13 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,  				struct sg_table *sgt,  				enum dma_data_direction dir); +/* low-level interface prime helpers */ +struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object * +exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, +				     struct dma_buf_attachment *attach, +				     struct sg_table *sgt); +void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj); +void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +  #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 8040ed2a831f..808a0a013780 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -582,9 +582,17 @@ static int gsc_src_set_transf(struct device *dev,  		break;  	case EXYNOS_DRM_DEGREE_180:  		cfg |= GSC_IN_ROT_180; +		if (flip & EXYNOS_DRM_FLIP_VERTICAL) +			cfg &= ~GSC_IN_ROT_XFLIP; +		if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) +			cfg &= ~GSC_IN_ROT_YFLIP;  		break;  	case EXYNOS_DRM_DEGREE_270:  		cfg |= GSC_IN_ROT_270; +		if (flip & EXYNOS_DRM_FLIP_VERTICAL) +			cfg &= ~GSC_IN_ROT_XFLIP; +		if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) +			cfg &= ~GSC_IN_ROT_YFLIP;  		break;  	default:  		dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); @@ -593,8 +601,7 @@ static int gsc_src_set_transf(struct device *dev,  	gsc_write(cfg, GSC_IN_CON); -	ctx->rotation = cfg & -		(GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; +	ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;  	*swap = ctx->rotation;  	return 0; @@ -846,9 +853,17 @@ static int gsc_dst_set_transf(struct device *dev,  		break;  	case EXYNOS_DRM_DEGREE_180:  		cfg |= GSC_IN_ROT_180; +		if (flip & EXYNOS_DRM_FLIP_VERTICAL) +			cfg &= ~GSC_IN_ROT_XFLIP; +		if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) +			cfg &= ~GSC_IN_ROT_YFLIP;  		break;  	case EXYNOS_DRM_DEGREE_270:  		cfg |= GSC_IN_ROT_270; +		if (flip & EXYNOS_DRM_FLIP_VERTICAL) +			cfg &= ~GSC_IN_ROT_XFLIP; +		if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) +			cfg &= ~GSC_IN_ROT_YFLIP;  		break;  	default:  		dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); @@ -857,8 +872,7 @@ static int gsc_dst_set_transf(struct device *dev,  	gsc_write(cfg, GSC_IN_CON); -	ctx->rotation = cfg & -		(GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; +	ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;  	*swap = ctx->rotation;  	return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index d4ec7465e9cc..055e8ec2ef21 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -87,10 +87,8 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,  	struct device *dev = drm_dev->dev;  	int ret; -	if (!dev->archdata.mapping) { -		DRM_ERROR("iommu_mapping is null.\n"); -		return -EFAULT; -	} +	if (!dev->archdata.mapping) +		return 0;  	subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev,  					sizeof(*subdrv_dev->dma_parms), @@ -144,17 +142,3 @@ void drm_iommu_detach_device(struct drm_device *drm_dev,  	iommu_detach_device(mapping->domain, subdrv_dev);  	drm_release_iommu_mapping(drm_dev);  } - -int drm_iommu_attach_device_if_possible(struct exynos_drm_crtc *exynos_crtc, -			struct drm_device *drm_dev, struct device *subdrv_dev) -{ -	int ret = 0; - -	if (is_drm_iommu_supported(drm_dev)) { -		if (exynos_crtc->ops->clear_channels) -			exynos_crtc->ops->clear_channels(exynos_crtc); -		return drm_iommu_attach_device(drm_dev, subdrv_dev); -	} - -	return ret; -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h index 8341c7a475b4..dc1b5441f491 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h @@ -29,19 +29,11 @@ void drm_iommu_detach_device(struct drm_device *dev_dev,  static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)  { -#ifdef CONFIG_ARM_DMA_USE_IOMMU  	struct device *dev = drm_dev->dev;  	return dev->archdata.mapping ? true : false; -#else -	return false; -#endif  } -int drm_iommu_attach_device_if_possible( -		struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev, -		struct device *subdrv_dev); -  #else  static inline int drm_create_iommu_mapping(struct drm_device *drm_dev) @@ -69,12 +61,5 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)  	return false;  } -static inline int drm_iommu_attach_device_if_possible( -		struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev, -		struct device *subdrv_dev) -{ -	return 0; -} -  #endif  #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 67e5451e066f..67d24236e745 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -1622,12 +1622,10 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)  		INIT_LIST_HEAD(&ippdrv->cmd_list);  		mutex_init(&ippdrv->cmd_lock); -		if (is_drm_iommu_supported(drm_dev)) { -			ret = drm_iommu_attach_device(drm_dev, ippdrv->dev); -			if (ret) { -				DRM_ERROR("failed to activate iommu\n"); -				goto err; -			} +		ret = drm_iommu_attach_device(drm_dev, ippdrv->dev); +		if (ret) { +			DRM_ERROR("failed to activate iommu\n"); +			goto err;  		}  	} @@ -1637,8 +1635,7 @@ err:  	/* get ipp driver entry */  	list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,  						drv_list) { -		if (is_drm_iommu_supported(drm_dev)) -			drm_iommu_detach_device(drm_dev, ippdrv->dev); +		drm_iommu_detach_device(drm_dev, ippdrv->dev);  		ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,  				ippdrv->prop_list.ipp_id); @@ -1654,8 +1651,7 @@ static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)  	/* get ipp driver entry */  	list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) { -		if (is_drm_iommu_supported(drm_dev)) -			drm_iommu_detach_device(drm_dev, ippdrv->dev); +		drm_iommu_detach_device(drm_dev, ippdrv->dev);  		ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,  				ippdrv->prop_list.ipp_id); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index a729980d3c2f..714822441467 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -20,12 +20,6 @@  #include "exynos_drm_gem.h"  #include "exynos_drm_plane.h" -static const uint32_t formats[] = { -	DRM_FORMAT_XRGB8888, -	DRM_FORMAT_ARGB8888, -	DRM_FORMAT_NV12, -}; -  /*   * This function is to get X or Y size shown via screen. This needs length and   * start position of CRTC. @@ -97,29 +91,18 @@ static void exynos_plane_mode_set(struct drm_plane *plane,  	/* set drm framebuffer data. */  	exynos_plane->src_x = src_x;  	exynos_plane->src_y = src_y; -	exynos_plane->src_width = (actual_w * exynos_plane->h_ratio) >> 16; -	exynos_plane->src_height = (actual_h * exynos_plane->v_ratio) >> 16; -	exynos_plane->fb_width = fb->width; -	exynos_plane->fb_height = fb->height; -	exynos_plane->bpp = fb->bits_per_pixel; -	exynos_plane->pitch = fb->pitches[0]; -	exynos_plane->pixel_format = fb->pixel_format; +	exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16; +	exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16;  	/* set plane range to be displayed. */  	exynos_plane->crtc_x = crtc_x;  	exynos_plane->crtc_y = crtc_y; -	exynos_plane->crtc_width = actual_w; -	exynos_plane->crtc_height = actual_h; - -	/* set drm mode data. */ -	exynos_plane->mode_width = mode->hdisplay; -	exynos_plane->mode_height = mode->vdisplay; -	exynos_plane->refresh = mode->vrefresh; -	exynos_plane->scan_flag = mode->flags; +	exynos_plane->crtc_w = actual_w; +	exynos_plane->crtc_h = actual_h;  	DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",  			exynos_plane->crtc_x, exynos_plane->crtc_y, -			exynos_plane->crtc_width, exynos_plane->crtc_height); +			exynos_plane->crtc_w, exynos_plane->crtc_h);  	plane->crtc = crtc;  } @@ -143,17 +126,17 @@ static int exynos_plane_atomic_check(struct drm_plane *plane,  	if (!state->fb)  		return 0; -	nr = exynos_drm_fb_get_buf_cnt(state->fb); +	nr = drm_format_num_planes(state->fb->pixel_format);  	for (i = 0; i < nr; i++) { -		struct exynos_drm_gem_buf *buffer = -					exynos_drm_fb_buffer(state->fb, i); +		struct exynos_drm_gem_obj *obj = +					exynos_drm_fb_gem_obj(state->fb, i); -		if (!buffer) { -			DRM_DEBUG_KMS("buffer is null\n"); +		if (!obj) { +			DRM_DEBUG_KMS("gem object is null\n");  			return -EFAULT;  		} -		exynos_plane->dma_addr[i] = buffer->dma_addr + +		exynos_plane->dma_addr[i] = obj->dma_addr +  					    state->fb->offsets[i];  		DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n", @@ -179,8 +162,10 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,  			      state->src_x >> 16, state->src_y >> 16,  			      state->src_w >> 16, state->src_h >> 16); -	if (exynos_crtc->ops->win_commit) -		exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos); +	exynos_plane->pending_fb = state->fb; + +	if (exynos_crtc->ops->update_plane) +		exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);  }  static void exynos_plane_atomic_disable(struct drm_plane *plane, @@ -192,9 +177,9 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane,  	if (!old_state->crtc)  		return; -	if (exynos_crtc->ops->win_disable) -		exynos_crtc->ops->win_disable(exynos_crtc, -					      exynos_plane->zpos); +	if (exynos_crtc->ops->disable_plane) +		exynos_crtc->ops->disable_plane(exynos_crtc, +						exynos_plane);  }  static const struct drm_plane_helper_funcs plane_helper_funcs = { @@ -226,13 +211,14 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,  int exynos_plane_init(struct drm_device *dev,  		      struct exynos_drm_plane *exynos_plane,  		      unsigned long possible_crtcs, enum drm_plane_type type, +		      const uint32_t *formats, unsigned int fcount,  		      unsigned int zpos)  {  	int err;  	err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, -				       &exynos_plane_funcs, formats, -				       ARRAY_SIZE(formats), type); +				       &exynos_plane_funcs, formats, fcount, +				       type);  	if (err) {  		DRM_ERROR("failed to initialize plane\n");  		return err; diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 8c88ae983c38..476c9340b591 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -12,4 +12,5 @@  int exynos_plane_init(struct drm_device *dev,  		      struct exynos_drm_plane *exynos_plane,  		      unsigned long possible_crtcs, enum drm_plane_type type, +		      const uint32_t *formats, unsigned int fcount,  		      unsigned int zpos); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 3413393d8a16..75718e1bc3dd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -25,7 +25,6 @@  #include "exynos_drm_drv.h"  #include "exynos_drm_crtc.h"  #include "exynos_drm_plane.h" -#include "exynos_drm_encoder.h"  #include "exynos_drm_vidi.h"  /* vidi has totally three virtual windows. */ @@ -35,11 +34,10 @@  					connector)  struct vidi_context { -	struct exynos_drm_display	display; +	struct drm_encoder		encoder;  	struct platform_device		*pdev;  	struct drm_device		*drm_dev;  	struct exynos_drm_crtc		*crtc; -	struct drm_encoder		*encoder;  	struct drm_connector		connector;  	struct exynos_drm_plane		planes[WINDOWS_NR];  	struct edid			*raw_edid; @@ -55,9 +53,9 @@ struct vidi_context {  	int				pipe;  }; -static inline struct vidi_context *display_to_vidi(struct exynos_drm_display *d) +static inline struct vidi_context *encoder_to_vidi(struct drm_encoder *e)  { -	return container_of(d, struct vidi_context, display); +	return container_of(e, struct vidi_context, encoder);  }  static const char fake_edid_info[] = { @@ -85,6 +83,12 @@ static const char fake_edid_info[] = {  	0x00, 0x00, 0x00, 0x06  }; +static const uint32_t formats[] = { +	DRM_FORMAT_XRGB8888, +	DRM_FORMAT_ARGB8888, +	DRM_FORMAT_NV12, +}; +  static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)  {  	struct vidi_context *ctx = crtc->ctx; @@ -100,7 +104,7 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)  	/*  	 * in case of page flip request, vidi_finish_pageflip function  	 * will not be called because direct_vblank is true and then -	 * that function will be called by crtc_ops->win_commit callback +	 * that function will be called by crtc_ops->update_plane callback  	 */  	schedule_work(&ctx->work); @@ -118,19 +122,14 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)  		ctx->vblank_on = false;  } -static void vidi_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) +static void vidi_update_plane(struct exynos_drm_crtc *crtc, +			      struct exynos_drm_plane *plane)  {  	struct vidi_context *ctx = crtc->ctx; -	struct exynos_drm_plane *plane;  	if (ctx->suspended)  		return; -	if (win < 0 || win >= WINDOWS_NR) -		return; - -	plane = &ctx->planes[win]; -  	DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr);  	if (ctx->vblank_on) @@ -179,13 +178,14 @@ static const struct exynos_drm_crtc_ops vidi_crtc_ops = {  	.disable = vidi_disable,  	.enable_vblank = vidi_enable_vblank,  	.disable_vblank = vidi_disable_vblank, -	.win_commit = vidi_win_commit, +	.update_plane = vidi_update_plane,  };  static void vidi_fake_vblank_handler(struct work_struct *work)  {  	struct vidi_context *ctx = container_of(work, struct vidi_context,  					work); +	int win;  	if (ctx->pipe < 0)  		return; @@ -196,7 +196,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)  	mutex_lock(&ctx->lock);  	if (ctx->direct_vblank) { -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); +		drm_crtc_handle_vblank(&ctx->crtc->base);  		ctx->direct_vblank = false;  		mutex_unlock(&ctx->lock);  		return; @@ -204,7 +204,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work)  	mutex_unlock(&ctx->lock); -	exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); +	for (win = 0 ; win < WINDOWS_NR ; win++) { +		struct exynos_drm_plane *plane = &ctx->planes[win]; + +		if (!plane->pending_fb) +			continue; + +		exynos_drm_crtc_finish_update(ctx->crtc, plane); +	}  }  static int vidi_show_connection(struct device *dev, @@ -259,9 +266,7 @@ static DEVICE_ATTR(connection, 0644, vidi_show_connection,  int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,  				struct drm_file *file_priv)  { -	struct vidi_context *ctx = NULL; -	struct drm_encoder *encoder; -	struct exynos_drm_display *display; +	struct vidi_context *ctx = dev_get_drvdata(drm_dev->dev);  	struct drm_exynos_vidi_connection *vidi = data;  	if (!vidi) { @@ -274,21 +279,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,  		return -EINVAL;  	} -	list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, -								head) { -		display = exynos_drm_get_display(encoder); - -		if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) { -			ctx = display_to_vidi(display); -			break; -		} -	} - -	if (!ctx) { -		DRM_DEBUG_KMS("not found virtual device type encoder.\n"); -		return -EINVAL; -	} -  	if (ctx->connected == vidi->connection) {  		DRM_DEBUG_KMS("same connection request.\n");  		return -EINVAL; @@ -381,7 +371,7 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)  {  	struct vidi_context *ctx = ctx_from_connector(connector); -	return ctx->encoder; +	return &ctx->encoder;  }  static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { @@ -389,14 +379,12 @@ static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {  	.best_encoder = vidi_best_encoder,  }; -static int vidi_create_connector(struct exynos_drm_display *display, -				struct drm_encoder *encoder) +static int vidi_create_connector(struct drm_encoder *encoder)  { -	struct vidi_context *ctx = display_to_vidi(display); +	struct vidi_context *ctx = encoder_to_vidi(encoder);  	struct drm_connector *connector = &ctx->connector;  	int ret; -	ctx->encoder = encoder;  	connector->polled = DRM_CONNECTOR_POLL_HPD;  	ret = drm_connector_init(ctx->drm_dev, connector, @@ -413,19 +401,47 @@ static int vidi_create_connector(struct exynos_drm_display *display,  	return 0;  } +static bool exynos_vidi_mode_fixup(struct drm_encoder *encoder, +				 const struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void exynos_vidi_mode_set(struct drm_encoder *encoder, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode) +{ +} + +static void exynos_vidi_enable(struct drm_encoder *encoder) +{ +} + +static void exynos_vidi_disable(struct drm_encoder *encoder) +{ +} -static struct exynos_drm_display_ops vidi_display_ops = { -	.create_connector = vidi_create_connector, +static struct drm_encoder_helper_funcs exynos_vidi_encoder_helper_funcs = { +	.mode_fixup = exynos_vidi_mode_fixup, +	.mode_set = exynos_vidi_mode_set, +	.enable = exynos_vidi_enable, +	.disable = exynos_vidi_disable, +}; + +static struct drm_encoder_funcs exynos_vidi_encoder_funcs = { +	.destroy = drm_encoder_cleanup,  };  static int vidi_bind(struct device *dev, struct device *master, void *data)  {  	struct vidi_context *ctx = dev_get_drvdata(dev);  	struct drm_device *drm_dev = data; +	struct drm_encoder *encoder = &ctx->encoder;  	struct exynos_drm_plane *exynos_plane;  	enum drm_plane_type type;  	unsigned int zpos; -	int ret; +	int pipe, ret;  	vidi_ctx_initialize(ctx, drm_dev); @@ -433,7 +449,8 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)  		type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :  						DRM_PLANE_TYPE_OVERLAY;  		ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], -					1 << ctx->pipe, type, zpos); +					1 << ctx->pipe, type, formats, +					ARRAY_SIZE(formats), zpos);  		if (ret)  			return ret;  	} @@ -447,9 +464,24 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)  		return PTR_ERR(ctx->crtc);  	} -	ret = exynos_drm_create_enc_conn(drm_dev, &ctx->display); +	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev, +						  EXYNOS_DISPLAY_TYPE_VIDI); +	if (pipe < 0) +		return pipe; + +	encoder->possible_crtcs = 1 << pipe; + +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + +	drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs); + +	ret = vidi_create_connector(encoder);  	if (ret) { -		ctx->crtc->base.funcs->destroy(&ctx->crtc->base); +		DRM_ERROR("failed to create connector ret = %d\n", ret); +		drm_encoder_cleanup(encoder);  		return ret;  	} @@ -475,8 +507,6 @@ static int vidi_probe(struct platform_device *pdev)  	if (!ctx)  		return -ENOMEM; -	ctx->display.type = EXYNOS_DISPLAY_TYPE_VIDI; -	ctx->display.ops = &vidi_display_ops;  	ctx->default_win = 0;  	ctx->pdev = pdev; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 99e286489031..932f7fa240f8 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -22,7 +22,6 @@  #include "regs-hdmi.h"  #include <linux/kernel.h> -#include <linux/spinlock.h>  #include <linux/wait.h>  #include <linux/i2c.h>  #include <linux/platform_device.h> @@ -33,8 +32,8 @@  #include <linux/clk.h>  #include <linux/regulator/consumer.h>  #include <linux/io.h> -#include <linux/of.h>  #include <linux/of_address.h> +#include <linux/of_device.h>  #include <linux/of_gpio.h>  #include <linux/hdmi.h>  #include <linux/component.h> @@ -48,7 +47,6 @@  #include "exynos_mixer.h"  #include <linux/gpio.h> -#include <media/s5p_hdmi.h>  #define ctx_from_connector(c)	container_of(c, struct hdmi_context, connector) @@ -88,109 +86,14 @@ struct hdmi_resources {  	int				regul_count;  }; -struct hdmi_tg_regs { -	u8 cmd[1]; -	u8 h_fsz[2]; -	u8 hact_st[2]; -	u8 hact_sz[2]; -	u8 v_fsz[2]; -	u8 vsync[2]; -	u8 vsync2[2]; -	u8 vact_st[2]; -	u8 vact_sz[2]; -	u8 field_chg[2]; -	u8 vact_st2[2]; -	u8 vact_st3[2]; -	u8 vact_st4[2]; -	u8 vsync_top_hdmi[2]; -	u8 vsync_bot_hdmi[2]; -	u8 field_top_hdmi[2]; -	u8 field_bot_hdmi[2]; -	u8 tg_3d[1]; -}; - -struct hdmi_v13_core_regs { -	u8 h_blank[2]; -	u8 v_blank[3]; -	u8 h_v_line[3]; -	u8 vsync_pol[1]; -	u8 int_pro_mode[1]; -	u8 v_blank_f[3]; -	u8 h_sync_gen[3]; -	u8 v_sync_gen1[3]; -	u8 v_sync_gen2[3]; -	u8 v_sync_gen3[3]; -}; - -struct hdmi_v14_core_regs { -	u8 h_blank[2]; -	u8 v2_blank[2]; -	u8 v1_blank[2]; -	u8 v_line[2]; -	u8 h_line[2]; -	u8 hsync_pol[1]; -	u8 vsync_pol[1]; -	u8 int_pro_mode[1]; -	u8 v_blank_f0[2]; -	u8 v_blank_f1[2]; -	u8 h_sync_start[2]; -	u8 h_sync_end[2]; -	u8 v_sync_line_bef_2[2]; -	u8 v_sync_line_bef_1[2]; -	u8 v_sync_line_aft_2[2]; -	u8 v_sync_line_aft_1[2]; -	u8 v_sync_line_aft_pxl_2[2]; -	u8 v_sync_line_aft_pxl_1[2]; -	u8 v_blank_f2[2]; /* for 3D mode */ -	u8 v_blank_f3[2]; /* for 3D mode */ -	u8 v_blank_f4[2]; /* for 3D mode */ -	u8 v_blank_f5[2]; /* for 3D mode */ -	u8 v_sync_line_aft_3[2]; -	u8 v_sync_line_aft_4[2]; -	u8 v_sync_line_aft_5[2]; -	u8 v_sync_line_aft_6[2]; -	u8 v_sync_line_aft_pxl_3[2]; -	u8 v_sync_line_aft_pxl_4[2]; -	u8 v_sync_line_aft_pxl_5[2]; -	u8 v_sync_line_aft_pxl_6[2]; -	u8 vact_space_1[2]; -	u8 vact_space_2[2]; -	u8 vact_space_3[2]; -	u8 vact_space_4[2]; -	u8 vact_space_5[2]; -	u8 vact_space_6[2]; -}; - -struct hdmi_v13_conf { -	struct hdmi_v13_core_regs core; -	struct hdmi_tg_regs tg; -}; - -struct hdmi_v14_conf { -	struct hdmi_v14_core_regs core; -	struct hdmi_tg_regs tg; -}; - -struct hdmi_conf_regs { -	int pixel_clock; -	int cea_video_id; -	enum hdmi_picture_aspect aspect_ratio; -	union { -		struct hdmi_v13_conf v13_conf; -		struct hdmi_v14_conf v14_conf; -	} conf; -}; -  struct hdmi_context { -	struct exynos_drm_display	display; +	struct drm_encoder		encoder;  	struct device			*dev;  	struct drm_device		*drm_dev;  	struct drm_connector		connector; -	struct drm_encoder		*encoder;  	bool				hpd;  	bool				powered;  	bool				dvi_mode; -	struct mutex			hdmi_mutex;  	void __iomem			*regs;  	int				irq; @@ -201,22 +104,20 @@ struct hdmi_context {  	/* current hdmiphy conf regs */  	struct drm_display_mode		current_mode; -	struct hdmi_conf_regs		mode_conf; +	u8				cea_video_id;  	struct hdmi_resources		res; +	const struct hdmi_driver_data	*drv_data;  	int				hpd_gpio;  	void __iomem			*regs_hdmiphy; -	const struct hdmiphy_config		*phy_confs; -	unsigned int			phy_conf_count;  	struct regmap			*pmureg; -	enum hdmi_type			type;  }; -static inline struct hdmi_context *display_to_hdmi(struct exynos_drm_display *d) +static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)  { -	return container_of(d, struct hdmi_context, display); +	return container_of(e, struct hdmi_context, encoder);  }  struct hdmiphy_config { @@ -624,6 +525,16 @@ static inline void hdmi_reg_writeb(struct hdmi_context *hdata,  	writeb(value, hdata->regs + reg_id);  } +static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id, +				   int bytes, u32 val) +{ +	while (--bytes >= 0) { +		writeb(val & 0xff, hdata->regs + reg_id); +		val >>= 8; +		reg_id += 4; +	} +} +  static inline void hdmi_reg_writemask(struct hdmi_context *hdata,  				 u32 reg_id, u32 value, u32 mask)  { @@ -930,7 +841,7 @@ static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)  static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)  { -	if (hdata->type == HDMI_TYPE13) +	if (hdata->drv_data->type == HDMI_TYPE13)  		hdmi_v13_regs_dump(hdata, prefix);  	else  		hdmi_v14_regs_dump(hdata, prefix); @@ -957,7 +868,7 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,  	u32 hdr_sum;  	u8 chksum;  	u32 mod; -	u32 vic; +	u8 ar;  	mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);  	if (hdata->dvi_mode) { @@ -988,27 +899,22 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,  		 * Set the aspect ratio as per the mode, mentioned in  		 * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard  		 */ -		switch (hdata->mode_conf.aspect_ratio) { +		ar = hdata->current_mode.picture_aspect_ratio; +		switch (ar) {  		case HDMI_PICTURE_ASPECT_4_3: -			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), -					hdata->mode_conf.aspect_ratio | -					AVI_4_3_CENTER_RATIO); +			ar |= AVI_4_3_CENTER_RATIO;  			break;  		case HDMI_PICTURE_ASPECT_16_9: -			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), -					hdata->mode_conf.aspect_ratio | -					AVI_16_9_CENTER_RATIO); +			ar |= AVI_16_9_CENTER_RATIO;  			break;  		case HDMI_PICTURE_ASPECT_NONE:  		default: -			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), -					hdata->mode_conf.aspect_ratio | -					AVI_SAME_AS_PIC_ASPECT_RATIO); +			ar |= AVI_SAME_AS_PIC_ASPECT_RATIO;  			break;  		} +		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), ar); -		vic = hdata->mode_conf.cea_video_id; -		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic); +		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), hdata->cea_video_id);  		chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1),  					infoframe->any.length, hdr_sum); @@ -1038,10 +944,10 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,  {  	struct hdmi_context *hdata = ctx_from_connector(connector); -	hdata->hpd = gpio_get_value(hdata->hpd_gpio); +	if (gpio_get_value(hdata->hpd_gpio)) +		return connector_status_connected; -	return hdata->hpd ? connector_status_connected : -			connector_status_disconnected; +	return connector_status_disconnected;  }  static void hdmi_connector_destroy(struct drm_connector *connector) @@ -1064,6 +970,7 @@ static int hdmi_get_modes(struct drm_connector *connector)  {  	struct hdmi_context *hdata = ctx_from_connector(connector);  	struct edid *edid; +	int ret;  	if (!hdata->ddc_adpt)  		return -ENODEV; @@ -1079,15 +986,19 @@ static int hdmi_get_modes(struct drm_connector *connector)  	drm_mode_connector_update_edid_property(connector, edid); -	return drm_add_edid_modes(connector, edid); +	ret = drm_add_edid_modes(connector, edid); + +	kfree(edid); + +	return ret;  }  static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)  {  	int i; -	for (i = 0; i < hdata->phy_conf_count; i++) -		if (hdata->phy_confs[i].pixel_clock == pixel_clock) +	for (i = 0; i < hdata->drv_data->phy_conf_count; i++) +		if (hdata->drv_data->phy_confs[i].pixel_clock == pixel_clock)  			return i;  	DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock); @@ -1120,7 +1031,7 @@ static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)  {  	struct hdmi_context *hdata = ctx_from_connector(connector); -	return hdata->encoder; +	return &hdata->encoder;  }  static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { @@ -1129,14 +1040,12 @@ static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {  	.best_encoder = hdmi_best_encoder,  }; -static int hdmi_create_connector(struct exynos_drm_display *display, -			struct drm_encoder *encoder) +static int hdmi_create_connector(struct drm_encoder *encoder)  { -	struct hdmi_context *hdata = display_to_hdmi(display); +	struct hdmi_context *hdata = encoder_to_hdmi(encoder);  	struct drm_connector *connector = &hdata->connector;  	int ret; -	hdata->encoder = encoder;  	connector->interlace_allowed = true;  	connector->polled = DRM_CONNECTOR_POLL_HPD; @@ -1154,23 +1063,30 @@ static int hdmi_create_connector(struct exynos_drm_display *display,  	return 0;  } -static void hdmi_mode_fixup(struct exynos_drm_display *display, -				struct drm_connector *connector, -				const struct drm_display_mode *mode, -				struct drm_display_mode *adjusted_mode) +static bool hdmi_mode_fixup(struct drm_encoder *encoder, +			    const struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode)  { +	struct drm_device *dev = encoder->dev; +	struct drm_connector *connector;  	struct drm_display_mode *m;  	int mode_ok; -	DRM_DEBUG_KMS("%s\n", __FILE__); -  	drm_mode_set_crtcinfo(adjusted_mode, 0); +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (connector->encoder == encoder) +			break; +	} + +	if (connector->encoder != encoder) +		return true; +  	mode_ok = hdmi_mode_valid(connector, adjusted_mode);  	/* just return if user desired mode exists. */  	if (mode_ok == MODE_OK) -		return; +		return true;  	/*  	 * otherwise, find the most suitable mode among modes and change it @@ -1190,6 +1106,8 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display,  			break;  		}  	} + +	return true;  }  static void hdmi_set_acr(u32 freq, u8 *acr) @@ -1252,7 +1170,7 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)  	hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]);  	hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]); -	if (hdata->type == HDMI_TYPE13) +	if (hdata->drv_data->type == HDMI_TYPE13)  		hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4);  	else  		hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); @@ -1386,7 +1304,7 @@ static void hdmi_conf_init(struct hdmi_context *hdata)  				HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);  	} -	if (hdata->type == HDMI_TYPE13) { +	if (hdata->drv_data->type == HDMI_TYPE13) {  		/* choose bluescreen (fecal) color */  		hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);  		hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34); @@ -1419,66 +1337,94 @@ static void hdmi_conf_init(struct hdmi_context *hdata)  static void hdmi_v13_mode_apply(struct hdmi_context *hdata)  { -	const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg; -	const struct hdmi_v13_core_regs *core = -		&hdata->mode_conf.conf.v13_conf.core; +	struct drm_display_mode *m = &hdata->current_mode; +	unsigned int val;  	int tries; -	/* setting core registers */ -	hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); -	hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]); -	hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); -	hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); -	hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); +	hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3, +			(m->htotal << 12) | m->vtotal); + +	val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0; +	hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val); + +	val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0; +	hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val); + +	val = (m->hsync_start - m->hdisplay - 2); +	val |= ((m->hsync_end - m->hdisplay - 2) << 10); +	val |= ((m->flags & DRM_MODE_FLAG_NHSYNC)  ? 1 : 0)<<20; +	hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val); + +	/* +	 * Quirk requirement for exynos HDMI IP design, +	 * 2 pixels less than the actual calculation for hsync_start +	 * and end. +	 */ + +	/* Following values & calculations differ for different type of modes */ +	if (m->flags & DRM_MODE_FLAG_INTERLACE) { +		/* Interlaced Mode */ +		val = ((m->vsync_end - m->vdisplay) / 2); +		val |= ((m->vsync_start - m->vdisplay) / 2) << 12; +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val); + +		val = m->vtotal / 2; +		val |= ((m->vtotal - m->vdisplay) / 2) << 11; +		hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val); + +		val = (m->vtotal + +			((m->vsync_end - m->vsync_start) * 4) + 5) / 2; +		val |= m->vtotal << 11; +		hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val); + +		val = ((m->vtotal / 2) + 7); +		val |= ((m->vtotal / 2) + 2) << 12; +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val); + +		val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay)); +		val |= ((m->htotal / 2) + +			(m->hsync_start - m->hdisplay)) << 12; +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val); + +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2, +				(m->vtotal - m->vdisplay) / 2); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2); + +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249); +	} else { +		/* Progressive Mode */ + +		val = m->vtotal; +		val |= (m->vtotal - m->vdisplay) << 11; +		hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val); + +		hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0); + +		val = (m->vsync_end - m->vdisplay); +		val |= ((m->vsync_start - m->vdisplay) << 12); +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val); + +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001); +		hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2, +				m->vtotal - m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248); +	} +  	/* Timing generator registers */ -	hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi[1]); +	hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal); +	hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233); +	hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233); +	hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233);  	/* waiting for HDMIPHY's PLL to get to steady state */  	for (tries = 100; tries; --tries) { @@ -1503,144 +1449,119 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)  static void hdmi_v14_mode_apply(struct hdmi_context *hdata)  { -	const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg; -	const struct hdmi_v14_core_regs *core = -		&hdata->mode_conf.conf.v14_conf.core; +	struct drm_display_mode *m = &hdata->current_mode;  	int tries; -	/* setting core registers */ -	hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); -	hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); -	hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]); -	hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]); -	hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]); -	hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]); -	hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]); -	hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]); -	hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]); -	hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]); -	hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]); -	hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); -	hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]); -	hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]); -	hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]); -	hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]); -	hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0, -			core->v_sync_line_bef_2[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1, -			core->v_sync_line_bef_2[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0, -			core->v_sync_line_bef_1[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1, -			core->v_sync_line_bef_1[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0, -			core->v_sync_line_aft_2[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1, -			core->v_sync_line_aft_2[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0, -			core->v_sync_line_aft_1[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1, -			core->v_sync_line_aft_1[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, -			core->v_sync_line_aft_pxl_2[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1, -			core->v_sync_line_aft_pxl_2[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, -			core->v_sync_line_aft_pxl_1[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1, -			core->v_sync_line_aft_pxl_1[1]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]); -	hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0, -			core->v_sync_line_aft_3[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1, -			core->v_sync_line_aft_3[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0, -			core->v_sync_line_aft_4[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1, -			core->v_sync_line_aft_4[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0, -			core->v_sync_line_aft_5[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1, -			core->v_sync_line_aft_5[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0, -			core->v_sync_line_aft_6[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1, -			core->v_sync_line_aft_6[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, -			core->v_sync_line_aft_pxl_3[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1, -			core->v_sync_line_aft_pxl_3[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, -			core->v_sync_line_aft_pxl_4[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1, -			core->v_sync_line_aft_pxl_4[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, -			core->v_sync_line_aft_pxl_5[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1, -			core->v_sync_line_aft_pxl_5[1]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, -			core->v_sync_line_aft_pxl_6[0]); -	hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1, -			core->v_sync_line_aft_pxl_6[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]); -	hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]); +	hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal); +	hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal); +	hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1, +			(m->flags & DRM_MODE_FLAG_NHSYNC)  ? 1 : 0); +	hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, +			(m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0); +	hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, +			(m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); + +	/* +	 * Quirk requirement for exynos 5 HDMI IP design, +	 * 2 pixels less than the actual calculation for hsync_start +	 * and end. +	 */ + +	/* Following values & calculations differ for different type of modes */ +	if (m->flags & DRM_MODE_FLAG_INTERLACE) { +		/* Interlaced Mode */ +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2, +			(m->vsync_end - m->vdisplay) / 2); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2, +			(m->vsync_start - m->vdisplay) / 2); +		hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2); +		hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2, +				(m->vtotal - m->vdisplay) / 2); +		hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, +				m->vtotal - m->vdisplay / 2); +		hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, +				(m->vtotal / 2) + 7); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, +				(m->vtotal / 2) + 2); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, +			(m->htotal / 2) + (m->hsync_start - m->hdisplay)); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, +			(m->htotal / 2) + (m->hsync_start - m->hdisplay)); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2, +				(m->vtotal - m->vdisplay) / 2); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, +				m->vtotal - m->vdisplay / 2); +		hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, +				(m->vtotal / 2) + 1); +		hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, +				(m->vtotal / 2) + 1); +		hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, +				(m->vtotal / 2) + 1); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0); +	} else { +		/* Progressive Mode */ +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2, +			m->vsync_end - m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2, +			m->vsync_start - m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal); +		hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2, +				m->vtotal - m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2, +				m->vtotal - m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x47b); +		hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x6ae); +		hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233); +		hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233); +		hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233); +	} + +	/* Following values & calculations are same irrespective of mode type */ +	hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2, +			m->hsync_start - m->hdisplay - 2); +	hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2, +			m->hsync_end - m->hdisplay - 2); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff); +	hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);  	/* Timing generator registers */ -	hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi[0]); -	hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi[1]); -	hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d[0]); +	hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal); +	hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay); +	hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233); +	hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1); +	hdmi_reg_writev(hdata, HDMI_TG_3D, 1, 0x0);  	/* waiting for HDMIPHY's PLL to get to steady state */  	for (tries = 100; tries; --tries) { @@ -1665,7 +1586,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)  static void hdmi_mode_apply(struct hdmi_context *hdata)  { -	if (hdata->type == HDMI_TYPE13) +	if (hdata->drv_data->type == HDMI_TYPE13)  		hdmi_v13_mode_apply(hdata);  	else  		hdmi_v14_mode_apply(hdata); @@ -1683,7 +1604,7 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)  	hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,  				HDMI_PHY_ENABLE_MODE_SET); -	if (hdata->type == HDMI_TYPE13) +	if (hdata->drv_data->type == HDMI_TYPE13)  		reg = HDMI_V13_PHY_RSTOUT;  	else  		reg = HDMI_PHY_RSTOUT; @@ -1697,7 +1618,7 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)  static void hdmiphy_poweron(struct hdmi_context *hdata)  { -	if (hdata->type != HDMI_TYPE14) +	if (hdata->drv_data->type != HDMI_TYPE14)  		return;  	DRM_DEBUG_KMS("\n"); @@ -1717,7 +1638,7 @@ static void hdmiphy_poweron(struct hdmi_context *hdata)  static void hdmiphy_poweroff(struct hdmi_context *hdata)  { -	if (hdata->type != HDMI_TYPE14) +	if (hdata->drv_data->type != HDMI_TYPE14)  		return;  	DRM_DEBUG_KMS("\n"); @@ -1743,13 +1664,14 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)  	int i;  	/* pixel clock */ -	i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock); +	i = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);  	if (i < 0) {  		DRM_ERROR("failed to find hdmiphy conf\n");  		return;  	} -	ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32); +	ret = hdmiphy_reg_write_buf(hdata, 0, +			hdata->drv_data->phy_confs[i].conf, 32);  	if (ret) {  		DRM_ERROR("failed to configure hdmiphy\n");  		return; @@ -1771,10 +1693,8 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)  	hdmiphy_conf_reset(hdata);  	hdmiphy_conf_apply(hdata); -	mutex_lock(&hdata->hdmi_mutex);  	hdmi_start(hdata, false);  	hdmi_conf_init(hdata); -	mutex_unlock(&hdata->hdmi_mutex);  	hdmi_audio_init(hdata); @@ -1785,271 +1705,32 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)  	hdmi_regs_dump(hdata, "start");  } -static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value) -{ -	int i; -	BUG_ON(num_bytes > 4); -	for (i = 0; i < num_bytes; i++) -		reg_pair[i] = (value >> (8 * i)) & 0xff; -} - -static void hdmi_v13_mode_set(struct hdmi_context *hdata, -			struct drm_display_mode *m) +static void hdmi_mode_set(struct drm_encoder *encoder, +			  struct drm_display_mode *mode, +			  struct drm_display_mode *adjusted_mode)  { -	struct hdmi_v13_core_regs *core = &hdata->mode_conf.conf.v13_conf.core; -	struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg; -	unsigned int val; - -	hdata->mode_conf.cea_video_id = -		drm_match_cea_mode((struct drm_display_mode *)m); -	hdata->mode_conf.pixel_clock = m->clock * 1000; -	hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio; - -	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay); -	hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal); - -	val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0; -	hdmi_set_reg(core->vsync_pol, 1, val); - -	val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0; -	hdmi_set_reg(core->int_pro_mode, 1, val); - -	val = (m->hsync_start - m->hdisplay - 2); -	val |= ((m->hsync_end - m->hdisplay - 2) << 10); -	val |= ((m->flags & DRM_MODE_FLAG_NHSYNC)  ? 1 : 0)<<20; -	hdmi_set_reg(core->h_sync_gen, 3, val); - -	/* -	 * Quirk requirement for exynos HDMI IP design, -	 * 2 pixels less than the actual calculation for hsync_start -	 * and end. -	 */ - -	/* Following values & calculations differ for different type of modes */ -	if (m->flags & DRM_MODE_FLAG_INTERLACE) { -		/* Interlaced Mode */ -		val = ((m->vsync_end - m->vdisplay) / 2); -		val |= ((m->vsync_start - m->vdisplay) / 2) << 12; -		hdmi_set_reg(core->v_sync_gen1, 3, val); - -		val = m->vtotal / 2; -		val |= ((m->vtotal - m->vdisplay) / 2) << 11; -		hdmi_set_reg(core->v_blank, 3, val); - -		val = (m->vtotal + -			((m->vsync_end - m->vsync_start) * 4) + 5) / 2; -		val |= m->vtotal << 11; -		hdmi_set_reg(core->v_blank_f, 3, val); - -		val = ((m->vtotal / 2) + 7); -		val |= ((m->vtotal / 2) + 2) << 12; -		hdmi_set_reg(core->v_sync_gen2, 3, val); - -		val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay)); -		val |= ((m->htotal / 2) + -			(m->hsync_start - m->hdisplay)) << 12; -		hdmi_set_reg(core->v_sync_gen3, 3, val); - -		hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2); -		hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2); - -		hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/ -	} else { -		/* Progressive Mode */ - -		val = m->vtotal; -		val |= (m->vtotal - m->vdisplay) << 11; -		hdmi_set_reg(core->v_blank, 3, val); - -		hdmi_set_reg(core->v_blank_f, 3, 0); - -		val = (m->vsync_end - m->vdisplay); -		val |= ((m->vsync_start - m->vdisplay) << 12); -		hdmi_set_reg(core->v_sync_gen1, 3, val); - -		hdmi_set_reg(core->v_sync_gen2, 3, 0x1001);/* Reset value  */ -		hdmi_set_reg(core->v_sync_gen3, 3, 0x1001);/* Reset value  */ -		hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay); -		hdmi_set_reg(tg->vact_sz, 2, m->vdisplay); -		hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */ -	} - -	/* Timing generator registers */ -	hdmi_set_reg(tg->cmd, 1, 0x0); -	hdmi_set_reg(tg->h_fsz, 2, m->htotal); -	hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay); -	hdmi_set_reg(tg->hact_sz, 2, m->hdisplay); -	hdmi_set_reg(tg->v_fsz, 2, m->vtotal); -	hdmi_set_reg(tg->vsync, 2, 0x1); -	hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */ -	hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */ -	hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */ -	hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */ -	hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */ -	hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */ -	hdmi_set_reg(tg->tg_3d, 1, 0x0); /* Not used */ -} - -static void hdmi_v14_mode_set(struct hdmi_context *hdata, -			struct drm_display_mode *m) -{ -	struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg; -	struct hdmi_v14_core_regs *core = -		&hdata->mode_conf.conf.v14_conf.core; - -	hdata->mode_conf.cea_video_id = -		drm_match_cea_mode((struct drm_display_mode *)m); -	hdata->mode_conf.pixel_clock = m->clock * 1000; -	hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio; - -	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay); -	hdmi_set_reg(core->v_line, 2, m->vtotal); -	hdmi_set_reg(core->h_line, 2, m->htotal); -	hdmi_set_reg(core->hsync_pol, 1, -			(m->flags & DRM_MODE_FLAG_NHSYNC)  ? 1 : 0); -	hdmi_set_reg(core->vsync_pol, 1, -			(m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0); -	hdmi_set_reg(core->int_pro_mode, 1, -			(m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); - -	/* -	 * Quirk requirement for exynos 5 HDMI IP design, -	 * 2 pixels less than the actual calculation for hsync_start -	 * and end. -	 */ - -	/* Following values & calculations differ for different type of modes */ -	if (m->flags & DRM_MODE_FLAG_INTERLACE) { -		/* Interlaced Mode */ -		hdmi_set_reg(core->v_sync_line_bef_2, 2, -			(m->vsync_end - m->vdisplay) / 2); -		hdmi_set_reg(core->v_sync_line_bef_1, 2, -			(m->vsync_start - m->vdisplay) / 2); -		hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2); -		hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2); -		hdmi_set_reg(core->v_blank_f0, 2, m->vtotal - m->vdisplay / 2); -		hdmi_set_reg(core->v_blank_f1, 2, m->vtotal); -		hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7); -		hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2); -		hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, -			(m->htotal / 2) + (m->hsync_start - m->hdisplay)); -		hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, -			(m->htotal / 2) + (m->hsync_start - m->hdisplay)); -		hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2); -		hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2); -		hdmi_set_reg(tg->vact_st2, 2, m->vtotal - m->vdisplay / 2); -		hdmi_set_reg(tg->vsync2, 2, (m->vtotal / 2) + 1); -		hdmi_set_reg(tg->vsync_bot_hdmi, 2, (m->vtotal / 2) + 1); -		hdmi_set_reg(tg->field_bot_hdmi, 2, (m->vtotal / 2) + 1); -		hdmi_set_reg(tg->vact_st3, 2, 0x0); -		hdmi_set_reg(tg->vact_st4, 2, 0x0); -	} else { -		/* Progressive Mode */ -		hdmi_set_reg(core->v_sync_line_bef_2, 2, -			m->vsync_end - m->vdisplay); -		hdmi_set_reg(core->v_sync_line_bef_1, 2, -			m->vsync_start - m->vdisplay); -		hdmi_set_reg(core->v2_blank, 2, m->vtotal); -		hdmi_set_reg(core->v1_blank, 2, m->vtotal - m->vdisplay); -		hdmi_set_reg(core->v_blank_f0, 2, 0xffff); -		hdmi_set_reg(core->v_blank_f1, 2, 0xffff); -		hdmi_set_reg(core->v_sync_line_aft_2, 2, 0xffff); -		hdmi_set_reg(core->v_sync_line_aft_1, 2, 0xffff); -		hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, 0xffff); -		hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, 0xffff); -		hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay); -		hdmi_set_reg(tg->vact_sz, 2, m->vdisplay); -		hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */ -		hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */ -		hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */ -		hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */ -		hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */ -		hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */ -	} - -	/* Following values & calculations are same irrespective of mode type */ -	hdmi_set_reg(core->h_sync_start, 2, m->hsync_start - m->hdisplay - 2); -	hdmi_set_reg(core->h_sync_end, 2, m->hsync_end - m->hdisplay - 2); -	hdmi_set_reg(core->vact_space_1, 2, 0xffff); -	hdmi_set_reg(core->vact_space_2, 2, 0xffff); -	hdmi_set_reg(core->vact_space_3, 2, 0xffff); -	hdmi_set_reg(core->vact_space_4, 2, 0xffff); -	hdmi_set_reg(core->vact_space_5, 2, 0xffff); -	hdmi_set_reg(core->vact_space_6, 2, 0xffff); -	hdmi_set_reg(core->v_blank_f2, 2, 0xffff); -	hdmi_set_reg(core->v_blank_f3, 2, 0xffff); -	hdmi_set_reg(core->v_blank_f4, 2, 0xffff); -	hdmi_set_reg(core->v_blank_f5, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_3, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_4, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_5, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_6, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_pxl_3, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_pxl_4, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_pxl_5, 2, 0xffff); -	hdmi_set_reg(core->v_sync_line_aft_pxl_6, 2, 0xffff); - -	/* Timing generator registers */ -	hdmi_set_reg(tg->cmd, 1, 0x0); -	hdmi_set_reg(tg->h_fsz, 2, m->htotal); -	hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay); -	hdmi_set_reg(tg->hact_sz, 2, m->hdisplay); -	hdmi_set_reg(tg->v_fsz, 2, m->vtotal); -	hdmi_set_reg(tg->vsync, 2, 0x1); -	hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */ -	hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */ -	hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */ -	hdmi_set_reg(tg->tg_3d, 1, 0x0); -} - -static void hdmi_mode_set(struct exynos_drm_display *display, -			struct drm_display_mode *mode) -{ -	struct hdmi_context *hdata = display_to_hdmi(display); -	struct drm_display_mode *m = mode; +	struct hdmi_context *hdata = encoder_to_hdmi(encoder); +	struct drm_display_mode *m = adjusted_mode;  	DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",  		m->hdisplay, m->vdisplay,  		m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?  		"INTERLACED" : "PROGRESSIVE"); -	/* preserve mode information for later use. */ -	drm_mode_copy(&hdata->current_mode, mode); - -	if (hdata->type == HDMI_TYPE13) -		hdmi_v13_mode_set(hdata, mode); -	else -		hdmi_v14_mode_set(hdata, mode); -} - -static void hdmi_commit(struct exynos_drm_display *display) -{ -	struct hdmi_context *hdata = display_to_hdmi(display); - -	mutex_lock(&hdata->hdmi_mutex); -	if (!hdata->powered) { -		mutex_unlock(&hdata->hdmi_mutex); -		return; -	} -	mutex_unlock(&hdata->hdmi_mutex); - -	hdmi_conf_apply(hdata); +	drm_mode_copy(&hdata->current_mode, m); +	hdata->cea_video_id = drm_match_cea_mode(mode);  } -static void hdmi_poweron(struct hdmi_context *hdata) +static void hdmi_enable(struct drm_encoder *encoder)  { +	struct hdmi_context *hdata = encoder_to_hdmi(encoder);  	struct hdmi_resources *res = &hdata->res; -	mutex_lock(&hdata->hdmi_mutex); -	if (hdata->powered) { -		mutex_unlock(&hdata->hdmi_mutex); +	if (hdata->powered)  		return; -	}  	hdata->powered = true; -	mutex_unlock(&hdata->hdmi_mutex); -  	pm_runtime_get_sync(hdata->dev);  	if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) @@ -2063,17 +1744,32 @@ static void hdmi_poweron(struct hdmi_context *hdata)  	clk_prepare_enable(res->sclk_hdmi);  	hdmiphy_poweron(hdata); -	hdmi_commit(&hdata->display); +	hdmi_conf_apply(hdata);  } -static void hdmi_poweroff(struct hdmi_context *hdata) +static void hdmi_disable(struct drm_encoder *encoder)  { +	struct hdmi_context *hdata = encoder_to_hdmi(encoder);  	struct hdmi_resources *res = &hdata->res; +	struct drm_crtc *crtc = encoder->crtc; +	const struct drm_crtc_helper_funcs *funcs = NULL; -	mutex_lock(&hdata->hdmi_mutex);  	if (!hdata->powered) -		goto out; -	mutex_unlock(&hdata->hdmi_mutex); +		return; + +	/* +	 * The SFRs of VP and Mixer are updated by Vertical Sync of +	 * Timing generator which is a part of HDMI so the sequence +	 * to disable TV Subsystem should be as following, +	 *	VP -> Mixer -> HDMI +	 * +	 * Below codes will try to disable Mixer and VP(if used) +	 * prior to disabling HDMI. +	 */ +	if (crtc) +		funcs = crtc->helper_private; +	if (funcs && funcs->disable) +		(*funcs->disable)(crtc);  	/* HDMI System Disable */  	hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN); @@ -2093,57 +1789,18 @@ static void hdmi_poweroff(struct hdmi_context *hdata)  	pm_runtime_put_sync(hdata->dev); -	mutex_lock(&hdata->hdmi_mutex);  	hdata->powered = false; - -out: -	mutex_unlock(&hdata->hdmi_mutex);  } -static void hdmi_dpms(struct exynos_drm_display *display, int mode) -{ -	struct hdmi_context *hdata = display_to_hdmi(display); -	struct drm_encoder *encoder = hdata->encoder; -	struct drm_crtc *crtc = encoder->crtc; -	const struct drm_crtc_helper_funcs *funcs = NULL; - -	DRM_DEBUG_KMS("mode %d\n", mode); - -	switch (mode) { -	case DRM_MODE_DPMS_ON: -		hdmi_poweron(hdata); -		break; -	case DRM_MODE_DPMS_STANDBY: -	case DRM_MODE_DPMS_SUSPEND: -	case DRM_MODE_DPMS_OFF: -		/* -		 * The SFRs of VP and Mixer are updated by Vertical Sync of -		 * Timing generator which is a part of HDMI so the sequence -		 * to disable TV Subsystem should be as following, -		 *	VP -> Mixer -> HDMI -		 * -		 * Below codes will try to disable Mixer and VP(if used) -		 * prior to disabling HDMI. -		 */ -		if (crtc) -			funcs = crtc->helper_private; -		if (funcs && funcs->disable) -			(*funcs->disable)(crtc); - -		hdmi_poweroff(hdata); -		break; -	default: -		DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); -		break; -	} -} - -static struct exynos_drm_display_ops hdmi_display_ops = { -	.create_connector = hdmi_create_connector, +static struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {  	.mode_fixup	= hdmi_mode_fixup,  	.mode_set	= hdmi_mode_set, -	.dpms		= hdmi_dpms, -	.commit		= hdmi_commit, +	.enable		= hdmi_enable, +	.disable	= hdmi_disable, +}; + +static struct drm_encoder_funcs exynos_hdmi_encoder_funcs = { +	.destroy = drm_encoder_cleanup,  };  static void hdmi_hotplug_work_func(struct work_struct *work) @@ -2152,10 +1809,6 @@ static void hdmi_hotplug_work_func(struct work_struct *work)  	hdata = container_of(work, struct hdmi_context, hotplug_work.work); -	mutex_lock(&hdata->hdmi_mutex); -	hdata->hpd = gpio_get_value(hdata->hpd_gpio); -	mutex_unlock(&hdata->hdmi_mutex); -  	if (hdata->drm_dev)  		drm_helper_hpd_irq_event(hdata->drm_dev);  } @@ -2254,30 +1907,6 @@ fail:  	return ret;  } -static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata -					(struct device *dev) -{ -	struct device_node *np = dev->of_node; -	struct s5p_hdmi_platform_data *pd; -	u32 value; - -	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); -	if (!pd) -		goto err_data; - -	if (!of_find_property(np, "hpd-gpio", &value)) { -		DRM_ERROR("no hpd gpio property found\n"); -		goto err_data; -	} - -	pd->hpd_gpio = of_get_named_gpio(np, "hpd-gpio", 0); - -	return pd; - -err_data: -	return NULL; -} -  static struct of_device_id hdmi_match_types[] = {  	{  		.compatible = "samsung,exynos5-hdmi", @@ -2301,10 +1930,33 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)  {  	struct drm_device *drm_dev = data;  	struct hdmi_context *hdata = dev_get_drvdata(dev); +	struct drm_encoder *encoder = &hdata->encoder; +	int ret, pipe;  	hdata->drm_dev = drm_dev; -	return exynos_drm_create_enc_conn(drm_dev, &hdata->display); +	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev, +						  EXYNOS_DISPLAY_TYPE_HDMI); +	if (pipe < 0) +		return pipe; + +	encoder->possible_crtcs = 1 << pipe; + +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + +	drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs); + +	ret = hdmi_create_connector(encoder); +	if (ret) { +		DRM_ERROR("failed to create connector ret = %d\n", ret); +		drm_encoder_cleanup(encoder); +		return ret; +	} + +	return 0;  }  static void hdmi_unbind(struct device *dev, struct device *master, void *data) @@ -2338,43 +1990,30 @@ static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)  static int hdmi_probe(struct platform_device *pdev)  {  	struct device_node *ddc_node, *phy_node; -	struct s5p_hdmi_platform_data *pdata; -	struct hdmi_driver_data *drv_data;  	const struct of_device_id *match;  	struct device *dev = &pdev->dev;  	struct hdmi_context *hdata;  	struct resource *res;  	int ret; -	if (!dev->of_node) -		return -ENODEV; - -	pdata = drm_hdmi_dt_parse_pdata(dev); -	if (!pdata) -		return -EINVAL; -  	hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);  	if (!hdata)  		return -ENOMEM; -	hdata->display.type = EXYNOS_DISPLAY_TYPE_HDMI; -	hdata->display.ops = &hdmi_display_ops; - -	mutex_init(&hdata->hdmi_mutex); - -	platform_set_drvdata(pdev, hdata); - -	match = of_match_node(hdmi_match_types, dev->of_node); +	match = of_match_device(hdmi_match_types, dev);  	if (!match)  		return -ENODEV; -	drv_data = (struct hdmi_driver_data *)match->data; -	hdata->type = drv_data->type; -	hdata->phy_confs = drv_data->phy_confs; -	hdata->phy_conf_count = drv_data->phy_conf_count; +	hdata->drv_data = match->data; + +	platform_set_drvdata(pdev, hdata); -	hdata->hpd_gpio = pdata->hpd_gpio;  	hdata->dev = dev; +	hdata->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0); +	if (hdata->hpd_gpio < 0) { +		DRM_ERROR("cannot get hpd gpio property\n"); +		return hdata->hpd_gpio; +	}  	ret = hdmi_resources_init(hdata);  	if (ret) { @@ -2426,7 +2065,7 @@ out_get_ddc_adpt:  	}  out_get_phy_port: -	if (drv_data->is_apb_phy) { +	if (hdata->drv_data->is_apb_phy) {  		hdata->regs_hdmiphy = of_iomap(phy_node, 0);  		if (!hdata->regs_hdmiphy) {  			DRM_ERROR("failed to ioremap hdmi phy\n"); @@ -2449,8 +2088,6 @@ out_get_phy_port:  		goto err_hdmiphy;  	} -	hdata->hpd = gpio_get_value(hdata->hpd_gpio); -  	INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);  	ret = devm_request_threaded_irq(dev, hdata->irq, NULL, diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index cae98db33062..7f81cce966d4 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -43,6 +43,7 @@  #define MIXER_WIN_NR		3  #define MIXER_DEFAULT_WIN	0 +#define VP_DEFAULT_WIN		2  /* The pixelformats that are natively supported by the mixer. */  #define MXR_FORMAT_RGB565	4 @@ -69,6 +70,24 @@ enum mixer_version_id {  	MXR_VER_128_0_0_184,  }; +enum mixer_flag_bits { +	MXR_BIT_POWERED, +	MXR_BIT_VSYNC, +}; + +static const uint32_t mixer_formats[] = { +	DRM_FORMAT_XRGB4444, +	DRM_FORMAT_XRGB1555, +	DRM_FORMAT_RGB565, +	DRM_FORMAT_XRGB8888, +	DRM_FORMAT_ARGB8888, +}; + +static const uint32_t vp_formats[] = { +	DRM_FORMAT_NV12, +	DRM_FORMAT_NV21, +}; +  struct mixer_context {  	struct platform_device *pdev;  	struct device		*dev; @@ -76,13 +95,11 @@ struct mixer_context {  	struct exynos_drm_crtc	*crtc;  	struct exynos_drm_plane	planes[MIXER_WIN_NR];  	int			pipe; +	unsigned long		flags;  	bool			interlace; -	bool			powered;  	bool			vp_enabled;  	bool			has_sclk; -	u32			int_en; -	struct mutex		mixer_mutex;  	struct mixer_resources	mixer_res;  	enum mixer_version_id	mxr_ver;  	wait_queue_head_t	wait_vsync_queue; @@ -380,19 +397,20 @@ static void mixer_stop(struct mixer_context *ctx)  		usleep_range(10000, 12000);  } -static void vp_video_buffer(struct mixer_context *ctx, unsigned int win) +static void vp_video_buffer(struct mixer_context *ctx, +			    struct exynos_drm_plane *plane)  {  	struct mixer_resources *res = &ctx->mixer_res; +	struct drm_plane_state *state = plane->base.state; +	struct drm_framebuffer *fb = state->fb; +	struct drm_display_mode *mode = &state->crtc->mode;  	unsigned long flags; -	struct exynos_drm_plane *plane;  	dma_addr_t luma_addr[2], chroma_addr[2];  	bool tiled_mode = false;  	bool crcb_mode = false;  	u32 val; -	plane = &ctx->planes[win]; - -	switch (plane->pixel_format) { +	switch (fb->pixel_format) {  	case DRM_FORMAT_NV12:  		crcb_mode = false;  		break; @@ -401,21 +419,21 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)  		break;  	default:  		DRM_ERROR("pixel format for vp is wrong [%d].\n", -				plane->pixel_format); +				fb->pixel_format);  		return;  	}  	luma_addr[0] = plane->dma_addr[0];  	chroma_addr[0] = plane->dma_addr[1]; -	if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) { +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {  		ctx->interlace = true;  		if (tiled_mode) {  			luma_addr[1] = luma_addr[0] + 0x40;  			chroma_addr[1] = chroma_addr[0] + 0x40;  		} else { -			luma_addr[1] = luma_addr[0] + plane->pitch; -			chroma_addr[1] = chroma_addr[0] + plane->pitch; +			luma_addr[1] = luma_addr[0] + fb->pitches[0]; +			chroma_addr[1] = chroma_addr[0] + fb->pitches[0];  		}  	} else {  		ctx->interlace = false; @@ -436,25 +454,25 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)  	vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);  	/* setting size of input image */ -	vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(plane->pitch) | -		VP_IMG_VSIZE(plane->fb_height)); +	vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) | +		VP_IMG_VSIZE(fb->height));  	/* chroma height has to reduced by 2 to avoid chroma distorions */ -	vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(plane->pitch) | -		VP_IMG_VSIZE(plane->fb_height / 2)); +	vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) | +		VP_IMG_VSIZE(fb->height / 2)); -	vp_reg_write(res, VP_SRC_WIDTH, plane->src_width); -	vp_reg_write(res, VP_SRC_HEIGHT, plane->src_height); +	vp_reg_write(res, VP_SRC_WIDTH, plane->src_w); +	vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h);  	vp_reg_write(res, VP_SRC_H_POSITION,  			VP_SRC_H_POSITION_VAL(plane->src_x));  	vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y); -	vp_reg_write(res, VP_DST_WIDTH, plane->crtc_width); +	vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w);  	vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);  	if (ctx->interlace) { -		vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height / 2); +		vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2);  		vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);  	} else { -		vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height); +		vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h);  		vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);  	} @@ -469,9 +487,9 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)  	vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);  	vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]); -	mixer_cfg_scan(ctx, plane->mode_height); -	mixer_cfg_rgb_fmt(ctx, plane->mode_height); -	mixer_cfg_layer(ctx, win, true); +	mixer_cfg_scan(ctx, mode->vdisplay); +	mixer_cfg_rgb_fmt(ctx, mode->vdisplay); +	mixer_cfg_layer(ctx, plane->zpos, true);  	mixer_run(ctx);  	mixer_vsync_set_update(ctx, true); @@ -491,15 +509,15 @@ static void mixer_layer_update(struct mixer_context *ctx)  static int mixer_setup_scale(const struct exynos_drm_plane *plane,  		unsigned int *x_ratio, unsigned int *y_ratio)  { -	if (plane->crtc_width != plane->src_width) { -		if (plane->crtc_width == 2 * plane->src_width) +	if (plane->crtc_w != plane->src_w) { +		if (plane->crtc_w == 2 * plane->src_w)  			*x_ratio = 1;  		else  			goto fail;  	} -	if (plane->crtc_height != plane->src_height) { -		if (plane->crtc_height == 2 * plane->src_height) +	if (plane->crtc_h != plane->src_h) { +		if (plane->crtc_h == 2 * plane->src_h)  			*y_ratio = 1;  		else  			goto fail; @@ -512,20 +530,22 @@ fail:  	return -ENOTSUPP;  } -static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win) +static void mixer_graph_buffer(struct mixer_context *ctx, +			       struct exynos_drm_plane *plane)  {  	struct mixer_resources *res = &ctx->mixer_res; +	struct drm_plane_state *state = plane->base.state; +	struct drm_framebuffer *fb = state->fb; +	struct drm_display_mode *mode = &state->crtc->mode;  	unsigned long flags; -	struct exynos_drm_plane *plane; +	unsigned int win = plane->zpos;  	unsigned int x_ratio = 0, y_ratio = 0;  	unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;  	dma_addr_t dma_addr;  	unsigned int fmt;  	u32 val; -	plane = &ctx->planes[win]; - -	switch (plane->pixel_format) { +	switch (fb->pixel_format) {  	case DRM_FORMAT_XRGB4444:  		fmt = MXR_FORMAT_ARGB4444;  		break; @@ -557,12 +577,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)  	/* converting dma address base and source offset */  	dma_addr = plane->dma_addr[0] -		+ (plane->src_x * plane->bpp >> 3) -		+ (plane->src_y * plane->pitch); +		+ (plane->src_x * fb->bits_per_pixel >> 3) +		+ (plane->src_y * fb->pitches[0]);  	src_x_offset = 0;  	src_y_offset = 0; -	if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) +	if (mode->flags & DRM_MODE_FLAG_INTERLACE)  		ctx->interlace = true;  	else  		ctx->interlace = false; @@ -576,18 +596,18 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)  	/* setup geometry */  	mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), -			plane->pitch / (plane->bpp >> 3)); +			fb->pitches[0] / (fb->bits_per_pixel >> 3));  	/* setup display size */  	if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&  		win == MIXER_DEFAULT_WIN) { -		val  = MXR_MXR_RES_HEIGHT(plane->mode_height); -		val |= MXR_MXR_RES_WIDTH(plane->mode_width); +		val  = MXR_MXR_RES_HEIGHT(mode->vdisplay); +		val |= MXR_MXR_RES_WIDTH(mode->hdisplay);  		mixer_reg_write(res, MXR_RESOLUTION, val);  	} -	val  = MXR_GRP_WH_WIDTH(plane->src_width); -	val |= MXR_GRP_WH_HEIGHT(plane->src_height); +	val  = MXR_GRP_WH_WIDTH(plane->src_w); +	val |= MXR_GRP_WH_HEIGHT(plane->src_h);  	val |= MXR_GRP_WH_H_SCALE(x_ratio);  	val |= MXR_GRP_WH_V_SCALE(y_ratio);  	mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); @@ -605,8 +625,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)  	/* set buffer address to mixer */  	mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr); -	mixer_cfg_scan(ctx, plane->mode_height); -	mixer_cfg_rgb_fmt(ctx, plane->mode_height); +	mixer_cfg_scan(ctx, mode->vdisplay); +	mixer_cfg_rgb_fmt(ctx, mode->vdisplay);  	mixer_cfg_layer(ctx, win, true);  	/* layer update mandatory for mixer 16.0.33.0 */ @@ -710,6 +730,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)  	struct mixer_context *ctx = arg;  	struct mixer_resources *res = &ctx->mixer_res;  	u32 val, base, shadow; +	int win;  	spin_lock(&res->reg_slock); @@ -718,6 +739,10 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)  	/* handling VSYNC */  	if (val & MXR_INT_STATUS_VSYNC) { +		/* vsync interrupt use different bit for read and clear */ +		val |= MXR_INT_CLEAR_VSYNC; +		val &= ~MXR_INT_STATUS_VSYNC; +  		/* interlace scan need to check shadow register */  		if (ctx->interlace) {  			base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0)); @@ -731,8 +756,15 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)  				goto out;  		} -		drm_handle_vblank(ctx->drm_dev, ctx->pipe); -		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); +		drm_crtc_handle_vblank(&ctx->crtc->base); +		for (win = 0 ; win < MIXER_WIN_NR ; win++) { +			struct exynos_drm_plane *plane = &ctx->planes[win]; + +			if (!plane->pending_fb) +				continue; + +			exynos_drm_crtc_finish_update(ctx->crtc, plane); +		}  		/* set wait vsync event to zero and wake up queue. */  		if (atomic_read(&ctx->wait_vsync_event)) { @@ -743,11 +775,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)  out:  	/* clear interrupts */ -	if (~val & MXR_INT_EN_VSYNC) { -		/* vsync interrupt use different bit for read and clear */ -		val &= ~MXR_INT_EN_VSYNC; -		val |= MXR_INT_CLEAR_VSYNC; -	}  	mixer_reg_write(res, MXR_INT_STATUS, val);  	spin_unlock(&res->reg_slock); @@ -882,8 +909,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,  		}  	} -	ret = drm_iommu_attach_device_if_possible(mixer_ctx->crtc, drm_dev, -								mixer_ctx->dev); +	ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);  	if (ret)  		priv->pipe--; @@ -892,8 +918,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,  static void mixer_ctx_remove(struct mixer_context *mixer_ctx)  { -	if (is_drm_iommu_supported(mixer_ctx->drm_dev)) -		drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev); +	drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);  }  static int mixer_enable_vblank(struct exynos_drm_crtc *crtc) @@ -901,14 +926,13 @@ static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)  	struct mixer_context *mixer_ctx = crtc->ctx;  	struct mixer_resources *res = &mixer_ctx->mixer_res; -	if (!mixer_ctx->powered) { -		mixer_ctx->int_en |= MXR_INT_EN_VSYNC; +	__set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags); +	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))  		return 0; -	}  	/* enable vsync interrupt */ -	mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC, -			MXR_INT_EN_VSYNC); +	mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); +	mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);  	return 0;  } @@ -918,48 +942,48 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)  	struct mixer_context *mixer_ctx = crtc->ctx;  	struct mixer_resources *res = &mixer_ctx->mixer_res; +	__clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags); + +	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags)) +		return; +  	/* disable vsync interrupt */ +	mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);  	mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);  } -static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win) +static void mixer_update_plane(struct exynos_drm_crtc *crtc, +			       struct exynos_drm_plane *plane)  {  	struct mixer_context *mixer_ctx = crtc->ctx; -	DRM_DEBUG_KMS("win: %d\n", win); +	DRM_DEBUG_KMS("win: %d\n", plane->zpos); -	mutex_lock(&mixer_ctx->mixer_mutex); -	if (!mixer_ctx->powered) { -		mutex_unlock(&mixer_ctx->mixer_mutex); +	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))  		return; -	} -	mutex_unlock(&mixer_ctx->mixer_mutex); -	if (win > 1 && mixer_ctx->vp_enabled) -		vp_video_buffer(mixer_ctx, win); +	if (plane->zpos > 1 && mixer_ctx->vp_enabled) +		vp_video_buffer(mixer_ctx, plane);  	else -		mixer_graph_buffer(mixer_ctx, win); +		mixer_graph_buffer(mixer_ctx, plane);  } -static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win) +static void mixer_disable_plane(struct exynos_drm_crtc *crtc, +				struct exynos_drm_plane *plane)  {  	struct mixer_context *mixer_ctx = crtc->ctx;  	struct mixer_resources *res = &mixer_ctx->mixer_res;  	unsigned long flags; -	DRM_DEBUG_KMS("win: %d\n", win); +	DRM_DEBUG_KMS("win: %d\n", plane->zpos); -	mutex_lock(&mixer_ctx->mixer_mutex); -	if (!mixer_ctx->powered) { -		mutex_unlock(&mixer_ctx->mixer_mutex); +	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))  		return; -	} -	mutex_unlock(&mixer_ctx->mixer_mutex);  	spin_lock_irqsave(&res->reg_slock, flags);  	mixer_vsync_set_update(mixer_ctx, false); -	mixer_cfg_layer(mixer_ctx, win, false); +	mixer_cfg_layer(mixer_ctx, plane->zpos, false);  	mixer_vsync_set_update(mixer_ctx, true);  	spin_unlock_irqrestore(&res->reg_slock, flags); @@ -970,12 +994,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)  	struct mixer_context *mixer_ctx = crtc->ctx;  	int err; -	mutex_lock(&mixer_ctx->mixer_mutex); -	if (!mixer_ctx->powered) { -		mutex_unlock(&mixer_ctx->mixer_mutex); +	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))  		return; -	} -	mutex_unlock(&mixer_ctx->mixer_mutex);  	err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);  	if (err < 0) { @@ -1003,13 +1023,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)  	struct mixer_resources *res = &ctx->mixer_res;  	int ret; -	mutex_lock(&ctx->mixer_mutex); -	if (ctx->powered) { -		mutex_unlock(&ctx->mixer_mutex); +	if (test_bit(MXR_BIT_POWERED, &ctx->flags))  		return; -	} - -	mutex_unlock(&ctx->mixer_mutex);  	pm_runtime_get_sync(ctx->dev); @@ -1041,13 +1056,14 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)  		}  	} -	mutex_lock(&ctx->mixer_mutex); -	ctx->powered = true; -	mutex_unlock(&ctx->mixer_mutex); +	set_bit(MXR_BIT_POWERED, &ctx->flags);  	mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); -	mixer_reg_write(res, MXR_INT_EN, ctx->int_en); +	if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) { +		mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); +		mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); +	}  	mixer_win_reset(ctx);  } @@ -1057,24 +1073,16 @@ static void mixer_disable(struct exynos_drm_crtc *crtc)  	struct mixer_resources *res = &ctx->mixer_res;  	int i; -	mutex_lock(&ctx->mixer_mutex); -	if (!ctx->powered) { -		mutex_unlock(&ctx->mixer_mutex); +	if (!test_bit(MXR_BIT_POWERED, &ctx->flags))  		return; -	} -	mutex_unlock(&ctx->mixer_mutex);  	mixer_stop(ctx);  	mixer_regs_dump(ctx);  	for (i = 0; i < MIXER_WIN_NR; i++) -		mixer_win_disable(crtc, i); +		mixer_disable_plane(crtc, &ctx->planes[i]); -	ctx->int_en = mixer_reg_read(res, MXR_INT_EN); - -	mutex_lock(&ctx->mixer_mutex); -	ctx->powered = false; -	mutex_unlock(&ctx->mixer_mutex); +	clear_bit(MXR_BIT_POWERED, &ctx->flags);  	clk_disable_unprepare(res->hdmi);  	clk_disable_unprepare(res->mixer); @@ -1113,8 +1121,8 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = {  	.enable_vblank		= mixer_enable_vblank,  	.disable_vblank		= mixer_disable_vblank,  	.wait_for_vblank	= mixer_wait_for_vblank, -	.win_commit		= mixer_win_commit, -	.win_disable		= mixer_win_disable, +	.update_plane		= mixer_update_plane, +	.disable_plane		= mixer_disable_plane,  };  static struct mixer_drv_data exynos5420_mxr_drv_data = { @@ -1177,7 +1185,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)  	struct mixer_context *ctx = dev_get_drvdata(dev);  	struct drm_device *drm_dev = data;  	struct exynos_drm_plane *exynos_plane; -	enum drm_plane_type type;  	unsigned int zpos;  	int ret; @@ -1186,10 +1193,23 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)  		return ret;  	for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { +		enum drm_plane_type type; +		const uint32_t *formats; +		unsigned int fcount; +  		type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY :  						DRM_PLANE_TYPE_OVERLAY; +		if (zpos < VP_DEFAULT_WIN) { +			formats = mixer_formats; +			fcount = ARRAY_SIZE(mixer_formats); +		} else { +			formats = vp_formats; +			fcount = ARRAY_SIZE(vp_formats); +		} +  		ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], -					1 << ctx->pipe, type, zpos); +					1 << ctx->pipe, type, formats, fcount, +					zpos);  		if (ret)  			return ret;  	} @@ -1236,8 +1256,6 @@ static int mixer_probe(struct platform_device *pdev)  		return -ENOMEM;  	} -	mutex_init(&ctx->mixer_mutex); -  	if (dev->of_node) {  		const struct of_device_id *match;  |