exynos_drm_fimd.c 23.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/* exynos_drm_fimd.c
 *
 * Copyright (C) 2011 Samsung Electronics Co.Ltd
 * Authors:
 *	Joonyoung Shim <jy0922.shim@samsung.com>
 *	Inki Dae <inki.dae@samsung.com>
 *
 * 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.
 *
 */
14
#include <drm/drmP.h>
15 16 17 18

#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
19
#include <linux/of.h>
20
#include <linux/of_device.h>
21
#include <linux/pm_runtime.h>
22

23
#include <video/of_display_timing.h>
24
#include <video/of_videomode.h>
25
#include <video/samsung_fimd.h>
26 27 28 29 30
#include <drm/exynos_drm.h>

#include "exynos_drm_drv.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_crtc.h"
31
#include "exynos_drm_iommu.h"
32 33

/*
34
 * FIMD stands for Fully Interactive Mobile Display and
35 36 37 38 39
 * as a display controller, it transfers contents drawn on memory
 * to a LCD Panel through Display Interfaces such as RGB or
 * CPU Interface.
 */

40 41
#define FIMD_DEFAULT_FRAMERATE 60

42 43 44
/* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)
#define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16)
45 46 47 48 49 50
/*
 * size control register for hardware windows 0 and alpha control register
 * for hardware windows 1 ~ 4
 */
#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16)
/* size control register for hardware windows 1 ~ 2. */
51 52 53 54 55 56 57
#define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)

#define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(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)

/* color key control register for hardware window 1 ~ 4. */
58
#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))
59
/* color key value register for hardware window 1 ~ 4. */
60
#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
61 62 63 64

/* FIMD has totally five hardware windows. */
#define WINDOWS_NR	5

65
#define get_fimd_manager(mgr)	platform_get_drvdata(to_platform_device(dev))
66

67 68
struct fimd_driver_data {
	unsigned int timing_base;
69 70

	unsigned int has_shadowcon:1;
71
	unsigned int has_clksel:1;
72
	unsigned int has_limited_fmt:1;
73 74
};

75 76 77
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
	.timing_base = 0x0,
	.has_clksel = 1,
78
	.has_limited_fmt = 1,
79 80
};

81
static struct fimd_driver_data exynos4_fimd_driver_data = {
82
	.timing_base = 0x0,
83
	.has_shadowcon = 1,
84 85
};

86
static struct fimd_driver_data exynos5_fimd_driver_data = {
87
	.timing_base = 0x20000,
88
	.has_shadowcon = 1,
89 90
};

91 92 93
struct fimd_win_data {
	unsigned int		offset_x;
	unsigned int		offset_y;
94 95 96 97
	unsigned int		ovl_width;
	unsigned int		ovl_height;
	unsigned int		fb_width;
	unsigned int		fb_height;
98
	unsigned int		bpp;
99
	unsigned int		pixel_format;
I
Inki Dae 已提交
100
	dma_addr_t		dma_addr;
101 102
	unsigned int		buf_offsize;
	unsigned int		line_size;	/* bytes */
103
	bool			enabled;
104
	bool			resume;
105 106 107
};

struct fimd_context {
108
	struct device			*dev;
109
	struct drm_device		*drm_dev;
110 111 112
	struct clk			*bus_clk;
	struct clk			*lcd_clk;
	void __iomem			*regs;
113
	struct drm_display_mode		mode;
114 115 116 117 118
	struct fimd_win_data		win_data[WINDOWS_NR];
	unsigned int			default_win;
	unsigned long			irq_flags;
	u32				vidcon0;
	u32				vidcon1;
119
	bool				suspended;
120
	int				pipe;
121 122
	wait_queue_head_t		wait_vsync_queue;
	atomic_t			wait_vsync_event;
123

124
	struct exynos_drm_panel_info panel;
125
	struct fimd_driver_data *driver_data;
126 127
};

128
static const struct of_device_id fimd_driver_dt_match[] = {
129 130
	{ .compatible = "samsung,s3c6400-fimd",
	  .data = &s3c64xx_fimd_driver_data },
131
	{ .compatible = "samsung,exynos4210-fimd",
132
	  .data = &exynos4_fimd_driver_data },
133
	{ .compatible = "samsung,exynos5250-fimd",
134 135 136 137
	  .data = &exynos5_fimd_driver_data },
	{},
};

138 139 140
static inline struct fimd_driver_data *drm_fimd_get_driver_data(
	struct platform_device *pdev)
{
141 142 143
	const struct of_device_id *of_id =
			of_match_device(fimd_driver_dt_match, &pdev->dev);

144
	return (struct fimd_driver_data *)of_id->data;
145 146
}

147
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
148
			struct drm_device *drm_dev, int pipe)
149
{
150
	struct fimd_context *ctx = mgr->ctx;
151 152

	ctx->drm_dev = drm_dev;
153
	ctx->pipe = pipe;
154

155 156 157 158 159 160 161 162 163
	/*
	 * enable drm irq mode.
	 * - with irq_enabled = true, we can use the vblank feature.
	 *
	 * P.S. note that we wouldn't use drm irq handler but
	 *	just specific driver own one instead because
	 *	drm framework supports only one irq handler.
	 */
	drm_dev->irq_enabled = true;
164

165 166 167 168 169 170
	/*
	 * with vblank_disable_allowed = true, vblank interrupt will be disabled
	 * by drm timer once a current process gives up ownership of
	 * vblank event.(after drm_vblank_put function is called)
	 */
	drm_dev->vblank_disable_allowed = true;
171

172 173 174
	/* attach this sub driver to iommu mapping if supported. */
	if (is_drm_iommu_supported(ctx->drm_dev))
		drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
175

176
	return 0;
177 178
}

179
static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
180
{
181
	struct fimd_context *ctx = mgr->ctx;
182

183 184 185
	/* 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);
186 187
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
		const struct drm_display_mode *mode)
{
	unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
	u32 clkdiv;

	/* Find the clock divider value that gets us closest to ideal_clk */
	clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);

	return (clkdiv < 0x100) ? clkdiv : 0xff;
}

static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
		const struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	if (adjusted_mode->vrefresh == 0)
		adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;

	return true;
}

static void fimd_mode_set(struct exynos_drm_manager *mgr,
		const struct drm_display_mode *in_mode)
{
	struct fimd_context *ctx = mgr->ctx;

	drm_mode_copy(&ctx->mode, in_mode);
}

218
static void fimd_commit(struct exynos_drm_manager *mgr)
219
{
220
	struct fimd_context *ctx = mgr->ctx;
221
	struct drm_display_mode *mode = &ctx->mode;
222
	struct fimd_driver_data *driver_data;
223
	u32 val, clkdiv, vidcon1;
224
	int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
225

226
	driver_data = ctx->driver_data;
I
Inki Dae 已提交
227 228 229
	if (ctx->suspended)
		return;

230 231 232 233
	/* nothing to do if we haven't set the mode yet */
	if (mode->htotal == 0 || mode->vtotal == 0)
		return;

234 235 236 237 238 239 240
	/* setup polarity values */
	vidcon1 = ctx->vidcon1;
	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
		vidcon1 |= VIDCON1_INV_VSYNC;
	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
		vidcon1 |= VIDCON1_INV_HSYNC;
	writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
241 242

	/* setup vertical timing values. */
243
	vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
244 245
	vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
	vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
246 247 248 249

	val = VIDTCON0_VBPD(vbpd - 1) |
		VIDTCON0_VFPD(vfpd - 1) |
		VIDTCON0_VSPW(vsync_len - 1);
250
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
251 252

	/* setup horizontal timing values.  */
253
	hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
254 255
	hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
	hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
256 257 258 259

	val = VIDTCON1_HBPD(hbpd - 1) |
		VIDTCON1_HFPD(hfpd - 1) |
		VIDTCON1_HSPW(hsync_len - 1);
260
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
261 262

	/* setup horizontal and vertical display size. */
263 264 265 266
	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
267
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
268 269 270 271 272

	/* setup clock source, clock divider, enable dma. */
	val = ctx->vidcon0;
	val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);

273 274 275 276 277
	if (ctx->driver_data->has_clksel) {
		val &= ~VIDCON0_CLKSEL_MASK;
		val |= VIDCON0_CLKSEL_LCD;
	}

278 279 280
	clkdiv = fimd_calc_clkdiv(ctx, mode);
	if (clkdiv > 1)
		val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
281 282 283 284 285 286 287 288 289 290 291
	else
		val &= ~VIDCON0_CLKDIR;	/* 1:1 clock */

	/*
	 * fields of register with prefix '_F' would be updated
	 * at vsync(same as dma start)
	 */
	val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
	writel(val, ctx->regs + VIDCON0);
}

292
static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
293
{
294
	struct fimd_context *ctx = mgr->ctx;
295 296
	u32 val;

297 298 299
	if (ctx->suspended)
		return -EPERM;

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
	if (!test_and_set_bit(0, &ctx->irq_flags)) {
		val = readl(ctx->regs + VIDINTCON0);

		val |= VIDINTCON0_INT_ENABLE;
		val |= VIDINTCON0_INT_FRAME;

		val &= ~VIDINTCON0_FRAMESEL0_MASK;
		val |= VIDINTCON0_FRAMESEL0_VSYNC;
		val &= ~VIDINTCON0_FRAMESEL1_MASK;
		val |= VIDINTCON0_FRAMESEL1_NONE;

		writel(val, ctx->regs + VIDINTCON0);
	}

	return 0;
}

317
static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
318
{
319
	struct fimd_context *ctx = mgr->ctx;
320 321
	u32 val;

322 323 324
	if (ctx->suspended)
		return;

325 326 327 328 329 330 331 332 333 334
	if (test_and_clear_bit(0, &ctx->irq_flags)) {
		val = readl(ctx->regs + VIDINTCON0);

		val &= ~VIDINTCON0_INT_FRAME;
		val &= ~VIDINTCON0_INT_ENABLE;

		writel(val, ctx->regs + VIDINTCON0);
	}
}

335
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
336
{
337
	struct fimd_context *ctx = mgr->ctx;
338

339 340 341 342 343 344 345 346 347 348 349
	if (ctx->suspended)
		return;

	atomic_set(&ctx->wait_vsync_event, 1);

	/*
	 * wait for FIMD to signal VSYNC interrupt or return after
	 * timeout which is set to 50ms (refresh rate of 20).
	 */
	if (!wait_event_timeout(ctx->wait_vsync_queue,
				!atomic_read(&ctx->wait_vsync_event),
350
				HZ/20))
351 352 353
		DRM_DEBUG_KMS("vblank wait timed out.\n");
}

354 355
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
			struct exynos_drm_overlay *overlay)
356
{
357
	struct fimd_context *ctx = mgr->ctx;
358
	struct fimd_win_data *win_data;
359
	int win;
360
	unsigned long offset;
361 362

	if (!overlay) {
363
		DRM_ERROR("overlay is NULL\n");
364 365 366
		return;
	}

367 368 369 370
	win = overlay->zpos;
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

371
	if (win < 0 || win >= WINDOWS_NR)
372 373
		return;

374 375 376 377 378
	offset = overlay->fb_x * (overlay->bpp >> 3);
	offset += overlay->fb_y * overlay->pitch;

	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);

379
	win_data = &ctx->win_data[win];
380

381 382 383 384 385 386
	win_data->offset_x = overlay->crtc_x;
	win_data->offset_y = overlay->crtc_y;
	win_data->ovl_width = overlay->crtc_width;
	win_data->ovl_height = overlay->crtc_height;
	win_data->fb_width = overlay->fb_width;
	win_data->fb_height = overlay->fb_height;
S
Seung-Woo Kim 已提交
387
	win_data->dma_addr = overlay->dma_addr[0] + offset;
388
	win_data->bpp = overlay->bpp;
389
	win_data->pixel_format = overlay->pixel_format;
390 391 392 393 394 395 396 397
	win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
				(overlay->bpp >> 3);
	win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);

	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
			win_data->offset_x, win_data->offset_y);
	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
			win_data->ovl_width, win_data->ovl_height);
398
	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
399 400
	DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
			overlay->fb_width, overlay->crtc_width);
401 402
}

403
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
404 405 406 407 408 409
{
	struct fimd_win_data *win_data = &ctx->win_data[win];
	unsigned long val;

	val = WINCONx_ENWIN;

410 411 412 413 414 415 416 417 418
	/*
	 * In case of s3c64xx, window 0 doesn't support alpha channel.
	 * So the request format is ARGB8888 then change it to XRGB8888.
	 */
	if (ctx->driver_data->has_limited_fmt && !win) {
		if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
			win_data->pixel_format = DRM_FORMAT_XRGB8888;
	}

419 420
	switch (win_data->pixel_format) {
	case DRM_FORMAT_C8:
421 422 423 424
		val |= WINCON0_BPPMODE_8BPP_PALETTE;
		val |= WINCONx_BURSTLEN_8WORD;
		val |= WINCONx_BYTSWP;
		break;
425 426 427 428 429 430
	case DRM_FORMAT_XRGB1555:
		val |= WINCON0_BPPMODE_16BPP_1555;
		val |= WINCONx_HAWSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
	case DRM_FORMAT_RGB565:
431 432 433 434
		val |= WINCON0_BPPMODE_16BPP_565;
		val |= WINCONx_HAWSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
435
	case DRM_FORMAT_XRGB8888:
436 437 438 439
		val |= WINCON0_BPPMODE_24BPP_888;
		val |= WINCONx_WSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
440 441
	case DRM_FORMAT_ARGB8888:
		val |= WINCON1_BPPMODE_25BPP_A1888
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
			| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
		val |= WINCONx_WSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
	default:
		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");

		val |= WINCON0_BPPMODE_24BPP_888;
		val |= WINCONx_WSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
	}

	DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);

	writel(val, ctx->regs + WINCON(win));
}

460
static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
461 462 463 464 465 466 467 468 469 470 471 472
{
	unsigned int keycon0 = 0, keycon1 = 0;

	keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
			WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);

	keycon1 = WxKEYCON1_COLVAL(0xffffffff);

	writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
	writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
/**
 * shadow_protect_win() - disable updating values from shadow registers at vsync
 *
 * @win: window to protect registers for
 * @protect: 1 to protect (disable updates)
 */
static void fimd_shadow_protect_win(struct fimd_context *ctx,
							int win, bool protect)
{
	u32 reg, bits, val;

	if (ctx->driver_data->has_shadowcon) {
		reg = SHADOWCON;
		bits = SHADOWCON_WINx_PROTECT(win);
	} else {
		reg = PRTCON;
		bits = PRTCON_PROTECT;
	}

	val = readl(ctx->regs + reg);
	if (protect)
		val |= bits;
	else
		val &= ~bits;
	writel(val, ctx->regs + reg);
}

500
static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
501
{
502
	struct fimd_context *ctx = mgr->ctx;
503
	struct fimd_win_data *win_data;
504
	int win = zpos;
505
	unsigned long val, alpha, size;
506 507
	unsigned int last_x;
	unsigned int last_y;
508

I
Inki Dae 已提交
509 510 511
	if (ctx->suspended)
		return;

512 513 514
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

515
	if (win < 0 || win >= WINDOWS_NR)
516 517 518 519
		return;

	win_data = &ctx->win_data[win];

520 521 522 523 524 525
	/* If suspended, enable this on resume */
	if (ctx->suspended) {
		win_data->resume = true;
		return;
	}

526
	/*
527
	 * SHADOWCON/PRTCON register is used for enabling timing.
528 529 530 531 532 533 534 535 536
	 *
	 * 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.
	 */

	/* protect windows */
537
	fimd_shadow_protect_win(ctx, win, true);
538 539

	/* buffer start address */
I
Inki Dae 已提交
540
	val = (unsigned long)win_data->dma_addr;
541 542 543
	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));

	/* buffer end address */
544
	size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
I
Inki Dae 已提交
545
	val = (unsigned long)(win_data->dma_addr + size);
546 547 548
	writel(val, ctx->regs + VIDWx_BUF_END(win, 0));

	DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
I
Inki Dae 已提交
549
			(unsigned long)win_data->dma_addr, val, size);
550 551
	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
			win_data->ovl_width, win_data->ovl_height);
552 553 554

	/* buffer size */
	val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
555 556 557
		VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
		VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
		VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
558 559 560 561
	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));

	/* OSD position */
	val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
562 563 564
		VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
		VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
		VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
565 566
	writel(val, ctx->regs + VIDOSD_A(win));

567 568 569 570 571 572 573
	last_x = win_data->offset_x + win_data->ovl_width;
	if (last_x)
		last_x--;
	last_y = win_data->offset_y + win_data->ovl_height;
	if (last_y)
		last_y--;

574 575 576
	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);

577 578
	writel(val, ctx->regs + VIDOSD_B(win));

579
	DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
580
			win_data->offset_x, win_data->offset_y, last_x, last_y);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

	/* hardware window 0 doesn't support alpha channel. */
	if (win != 0) {
		/* OSD alpha */
		alpha = VIDISD14C_ALPHA1_R(0xf) |
			VIDISD14C_ALPHA1_G(0xf) |
			VIDISD14C_ALPHA1_B(0xf);

		writel(alpha, ctx->regs + VIDOSD_C(win));
	}

	/* OSD size */
	if (win != 3 && win != 4) {
		u32 offset = VIDOSD_D(win);
		if (win == 0)
596
			offset = VIDOSD_C(win);
597
		val = win_data->ovl_width * win_data->ovl_height;
598 599 600 601 602
		writel(val, ctx->regs + offset);

		DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
	}

603
	fimd_win_set_pixfmt(ctx, win);
604 605 606

	/* hardware window 0 doesn't support color key. */
	if (win != 0)
607
		fimd_win_set_colkey(ctx, win);
608

609 610 611 612 613
	/* wincon */
	val = readl(ctx->regs + WINCON(win));
	val |= WINCONx_ENWIN;
	writel(val, ctx->regs + WINCON(win));

614
	/* Enable DMA channel and unprotect windows */
615 616 617 618 619 620 621
	fimd_shadow_protect_win(ctx, win, false);

	if (ctx->driver_data->has_shadowcon) {
		val = readl(ctx->regs + SHADOWCON);
		val |= SHADOWCON_CHx_ENABLE(win);
		writel(val, ctx->regs + SHADOWCON);
	}
622 623

	win_data->enabled = true;
624 625
}

626
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
627
{
628
	struct fimd_context *ctx = mgr->ctx;
629
	struct fimd_win_data *win_data;
630
	int win = zpos;
631 632
	u32 val;

633 634 635
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

636
	if (win < 0 || win >= WINDOWS_NR)
637 638
		return;

639 640
	win_data = &ctx->win_data[win];

641 642 643 644 645 646
	if (ctx->suspended) {
		/* do not resume this window*/
		win_data->resume = false;
		return;
	}

647
	/* protect windows */
648
	fimd_shadow_protect_win(ctx, win, true);
649 650 651 652 653 654 655

	/* wincon */
	val = readl(ctx->regs + WINCON(win));
	val &= ~WINCONx_ENWIN;
	writel(val, ctx->regs + WINCON(win));

	/* unprotect windows */
656 657 658 659 660 661 662
	if (ctx->driver_data->has_shadowcon) {
		val = readl(ctx->regs + SHADOWCON);
		val &= ~SHADOWCON_CHx_ENABLE(win);
		writel(val, ctx->regs + SHADOWCON);
	}

	fimd_shadow_protect_win(ctx, win, false);
663 664

	win_data->enabled = false;
665 666
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
static void fimd_clear_win(struct fimd_context *ctx, int win)
{
	writel(0, ctx->regs + WINCON(win));
	writel(0, ctx->regs + VIDOSD_A(win));
	writel(0, ctx->regs + VIDOSD_B(win));
	writel(0, ctx->regs + VIDOSD_C(win));

	if (win == 1 || win == 2)
		writel(0, ctx->regs + VIDOSD_D(win));

	fimd_shadow_protect_win(ctx, win, false);
}

static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
	struct fimd_context *ctx = mgr->ctx;
	struct fimd_win_data *win_data;
	int i;

	for (i = 0; i < WINDOWS_NR; i++) {
		win_data = &ctx->win_data[i];
		win_data->resume = win_data->enabled;
		if (win_data->enabled)
			fimd_win_disable(mgr, i);
	}
	fimd_wait_for_vblank(mgr);
}

static void fimd_window_resume(struct exynos_drm_manager *mgr)
{
	struct fimd_context *ctx = mgr->ctx;
	struct fimd_win_data *win_data;
	int i;

	for (i = 0; i < WINDOWS_NR; i++) {
		win_data = &ctx->win_data[i];
		win_data->enabled = win_data->resume;
		win_data->resume = false;
	}
}

static void fimd_apply(struct exynos_drm_manager *mgr)
{
	struct fimd_context *ctx = mgr->ctx;
	struct fimd_win_data *win_data;
	int i;

	for (i = 0; i < WINDOWS_NR; i++) {
		win_data = &ctx->win_data[i];
		if (win_data->enabled)
			fimd_win_commit(mgr, i);
	}

	fimd_commit(mgr);
}

static int fimd_poweron(struct exynos_drm_manager *mgr)
{
	struct fimd_context *ctx = mgr->ctx;
	int ret;

	if (!ctx->suspended)
		return 0;

	ctx->suspended = false;

733 734
	pm_runtime_get_sync(ctx->dev);

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
	ret = clk_prepare_enable(ctx->bus_clk);
	if (ret < 0) {
		DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
		goto bus_clk_err;
	}

	ret = clk_prepare_enable(ctx->lcd_clk);
	if  (ret < 0) {
		DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
		goto lcd_clk_err;
	}

	/* if vblank was enabled status, enable it again. */
	if (test_and_clear_bit(0, &ctx->irq_flags)) {
		ret = fimd_enable_vblank(mgr);
		if (ret) {
			DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
			goto enable_vblank_err;
		}
	}

	fimd_window_resume(mgr);

	fimd_apply(mgr);

	return 0;

enable_vblank_err:
	clk_disable_unprepare(ctx->lcd_clk);
lcd_clk_err:
	clk_disable_unprepare(ctx->bus_clk);
bus_clk_err:
	ctx->suspended = true;
	return ret;
}

static int fimd_poweroff(struct exynos_drm_manager *mgr)
{
	struct fimd_context *ctx = mgr->ctx;

	if (ctx->suspended)
		return 0;

	/*
	 * We need to make sure that all windows are disabled before we
	 * suspend that connector. Otherwise we might try to scan from
	 * a destroyed buffer later.
	 */
	fimd_window_suspend(mgr);

	clk_disable_unprepare(ctx->lcd_clk);
	clk_disable_unprepare(ctx->bus_clk);

788 789
	pm_runtime_put_sync(ctx->dev);

790 791 792 793
	ctx->suspended = true;
	return 0;
}

794 795
static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
{
796
	DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
797 798 799

	switch (mode) {
	case DRM_MODE_DPMS_ON:
800
		fimd_poweron(mgr);
801 802 803 804
		break;
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
805
		fimd_poweroff(mgr);
806 807 808 809 810 811 812
		break;
	default:
		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
		break;
	}
}

813
static struct exynos_drm_manager_ops fimd_manager_ops = {
814
	.initialize = fimd_mgr_initialize,
815
	.remove = fimd_mgr_remove,
816
	.dpms = fimd_dpms,
817 818
	.mode_fixup = fimd_mode_fixup,
	.mode_set = fimd_mode_set,
819 820 821 822 823 824 825
	.commit = fimd_commit,
	.enable_vblank = fimd_enable_vblank,
	.disable_vblank = fimd_disable_vblank,
	.wait_for_vblank = fimd_wait_for_vblank,
	.win_mode_set = fimd_win_mode_set,
	.win_commit = fimd_win_commit,
	.win_disable = fimd_win_disable,
826 827
};

828
static struct exynos_drm_manager fimd_manager = {
829 830
	.type = EXYNOS_DISPLAY_TYPE_LCD,
	.ops = &fimd_manager_ops,
831 832
};

833 834 835 836 837 838 839 840 841 842 843
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
	struct fimd_context *ctx = (struct fimd_context *)dev_id;
	u32 val;

	val = readl(ctx->regs + VIDINTCON1);

	if (val & VIDINTCON1_INT_FRAME)
		/* VSYNC interrupt */
		writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);

844
	/* check the crtc is detached already from encoder */
845
	if (ctx->pipe < 0 || !ctx->drm_dev)
846
		goto out;
I
Inki Dae 已提交
847

848 849
	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
	exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
850

851 852 853
	/* set wait vsync event to zero and wake up queue. */
	if (atomic_read(&ctx->wait_vsync_event)) {
		atomic_set(&ctx->wait_vsync_event, 0);
854
		wake_up(&ctx->wait_vsync_queue);
855
	}
856
out:
857 858 859
	return IRQ_HANDLED;
}

860 861 862 863 864 865 866
static int fimd_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct fimd_context *ctx;
	struct resource *res;
	int win;
	int ret = -EINVAL;
867

868 869 870
	if (!dev->of_node)
		return -ENODEV;

871
	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
872 873 874
	if (!ctx)
		return -ENOMEM;

875
	ctx->dev = dev;
876
	ctx->suspended = true;
877

878 879 880 881
	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
		ctx->vidcon1 |= VIDCON1_INV_VDEN;
	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
		ctx->vidcon1 |= VIDCON1_INV_VCLK;
882

883 884 885 886 887 888 889 890 891 892 893
	ctx->bus_clk = devm_clk_get(dev, "fimd");
	if (IS_ERR(ctx->bus_clk)) {
		dev_err(dev, "failed to get bus clock\n");
		return PTR_ERR(ctx->bus_clk);
	}

	ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
	if (IS_ERR(ctx->lcd_clk)) {
		dev_err(dev, "failed to get lcd clock\n");
		return PTR_ERR(ctx->lcd_clk);
	}
894 895 896

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

897
	ctx->regs = devm_ioremap_resource(dev, res);
898 899
	if (IS_ERR(ctx->regs))
		return PTR_ERR(ctx->regs);
900

901
	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
902 903
	if (!res) {
		dev_err(dev, "irq request failed.\n");
904
		return -ENXIO;
905 906
	}

907
	ret = devm_request_irq(dev, res->start, fimd_irq_handler,
908 909
							0, "drm_fimd", ctx);
	if (ret) {
910
		dev_err(dev, "irq request failed.\n");
911
		return ret;
912 913
	}

914
	ctx->driver_data = drm_fimd_get_driver_data(pdev);
915
	init_waitqueue_head(&ctx->wait_vsync_queue);
916
	atomic_set(&ctx->wait_vsync_event, 0);
917

918
	platform_set_drvdata(pdev, &fimd_manager);
919

920 921 922
	fimd_manager.ctx = ctx;
	exynos_drm_manager_register(&fimd_manager);

923 924 925 926 927
	pm_runtime_enable(dev);

	for (win = 0; win < WINDOWS_NR; win++)
		fimd_clear_win(ctx, win);

928 929 930
	return 0;
}

931
static int fimd_remove(struct platform_device *pdev)
932
{
933
	struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
934

935
	exynos_drm_manager_unregister(&fimd_manager);
936

937
	fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
938

939
	pm_runtime_disable(&pdev->dev);
940 941

	return 0;
I
Inki Dae 已提交
942 943
}

944
struct platform_driver fimd_driver = {
945
	.probe		= fimd_probe,
946
	.remove		= fimd_remove,
947 948 949
	.driver		= {
		.name	= "exynos4-fb",
		.owner	= THIS_MODULE,
950
		.of_match_table = fimd_driver_dt_match,
951 952
	},
};