exynos_drm_fimd.c 25.8 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 bool fimd_display_is_connected(struct exynos_drm_display *display)
148 149 150 151 152 153
{
	/* TODO. */

	return true;
}

154
static void *fimd_get_panel(struct exynos_drm_display *display)
155
{
156
	struct fimd_context *ctx = display->ctx;
157

158
	return &ctx->panel;
159 160
}

161 162
static int fimd_check_mode(struct exynos_drm_display *display,
			struct drm_display_mode *mode)
163 164 165 166 167 168
{
	/* TODO. */

	return 0;
}

169
static struct exynos_drm_display_ops fimd_display_ops = {
170
	.is_connected = fimd_display_is_connected,
171
	.get_panel = fimd_get_panel,
172
	.check_mode = fimd_check_mode,
173 174
};

175 176 177 178 179
static struct exynos_drm_display fimd_display = {
	.type = EXYNOS_DISPLAY_TYPE_LCD,
	.ops = &fimd_display_ops,
};

180
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
181
			struct drm_device *drm_dev, int pipe)
182
{
183
	struct fimd_context *ctx = mgr->ctx;
184 185

	ctx->drm_dev = drm_dev;
186
	ctx->pipe = pipe;
187

188 189 190 191 192 193 194 195 196
	/*
	 * 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;
197

198 199 200 201 202 203
	/*
	 * 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;
204

205 206 207
	/* 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);
208

209
	return 0;
210 211
}

212
static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
213
{
214
	struct fimd_context *ctx = mgr->ctx;
215

216 217 218
	/* 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);
219 220
}

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
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);
}

251
static void fimd_commit(struct exynos_drm_manager *mgr)
252
{
253
	struct fimd_context *ctx = mgr->ctx;
254
	struct drm_display_mode *mode = &ctx->mode;
255
	struct fimd_driver_data *driver_data;
256 257
	u32 val, clkdiv;
	int hblank, vblank, vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
258

259
	driver_data = ctx->driver_data;
I
Inki Dae 已提交
260 261 262
	if (ctx->suspended)
		return;

263 264 265 266
	/* nothing to do if we haven't set the mode yet */
	if (mode->htotal == 0 || mode->vtotal == 0)
		return;

267
	/* setup polarity values from machine code. */
268
	writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
269 270

	/* setup vertical timing values. */
271 272 273 274 275 276 277 278
	vblank = mode->crtc_vblank_end - mode->crtc_vblank_start;
	vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
	vbpd = (vblank - vsync_len) / 2;
	vfpd = vblank - vsync_len - vbpd;

	val = VIDTCON0_VBPD(vbpd - 1) |
		VIDTCON0_VFPD(vfpd - 1) |
		VIDTCON0_VSPW(vsync_len - 1);
279
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
280 281

	/* setup horizontal timing values.  */
282 283 284 285 286 287 288 289
	hblank = mode->crtc_hblank_end - mode->crtc_hblank_start;
	hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
	hbpd = (hblank - hsync_len) / 2;
	hfpd = hblank - hsync_len - hbpd;

	val = VIDTCON1_HBPD(hbpd - 1) |
		VIDTCON1_HFPD(hfpd - 1) |
		VIDTCON1_HSPW(hsync_len - 1);
290
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
291 292

	/* setup horizontal and vertical display size. */
293 294 295 296
	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
	       VIDTCON2_HOZVAL(mode->hdisplay - 1) |
	       VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
	       VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
297
	writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
298 299 300 301 302

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

303 304 305 306 307
	if (ctx->driver_data->has_clksel) {
		val &= ~VIDCON0_CLKSEL_MASK;
		val |= VIDCON0_CLKSEL_LCD;
	}

308 309 310
	clkdiv = fimd_calc_clkdiv(ctx, mode);
	if (clkdiv > 1)
		val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
311 312 313 314 315 316 317 318 319 320 321
	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);
}

322
static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
323
{
324
	struct fimd_context *ctx = mgr->ctx;
325 326
	u32 val;

327 328 329
	if (ctx->suspended)
		return -EPERM;

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
	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;
}

347
static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
348
{
349
	struct fimd_context *ctx = mgr->ctx;
350 351
	u32 val;

352 353 354
	if (ctx->suspended)
		return;

355 356 357 358 359 360 361 362 363 364
	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);
	}
}

365
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
366
{
367
	struct fimd_context *ctx = mgr->ctx;
368

369 370 371 372 373 374 375 376 377 378 379
	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),
380
				HZ/20))
381 382 383
		DRM_DEBUG_KMS("vblank wait timed out.\n");
}

384 385
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
			struct exynos_drm_overlay *overlay)
386
{
387
	struct fimd_context *ctx = mgr->ctx;
388
	struct fimd_win_data *win_data;
389
	int win;
390
	unsigned long offset;
391 392

	if (!overlay) {
393
		DRM_ERROR("overlay is NULL\n");
394 395 396
		return;
	}

397 398 399 400
	win = overlay->zpos;
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

401
	if (win < 0 || win >= WINDOWS_NR)
402 403
		return;

404 405 406 407 408
	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);

409
	win_data = &ctx->win_data[win];
410

411 412 413 414 415 416
	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 已提交
417
	win_data->dma_addr = overlay->dma_addr[0] + offset;
418
	win_data->bpp = overlay->bpp;
419
	win_data->pixel_format = overlay->pixel_format;
420 421 422 423 424 425 426 427
	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);
428
	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
429 430
	DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
			overlay->fb_width, overlay->crtc_width);
431 432
}

433
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
434 435 436 437 438 439
{
	struct fimd_win_data *win_data = &ctx->win_data[win];
	unsigned long val;

	val = WINCONx_ENWIN;

440 441 442 443 444 445 446 447 448
	/*
	 * 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;
	}

449 450
	switch (win_data->pixel_format) {
	case DRM_FORMAT_C8:
451 452 453 454
		val |= WINCON0_BPPMODE_8BPP_PALETTE;
		val |= WINCONx_BURSTLEN_8WORD;
		val |= WINCONx_BYTSWP;
		break;
455 456 457 458 459 460
	case DRM_FORMAT_XRGB1555:
		val |= WINCON0_BPPMODE_16BPP_1555;
		val |= WINCONx_HAWSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
	case DRM_FORMAT_RGB565:
461 462 463 464
		val |= WINCON0_BPPMODE_16BPP_565;
		val |= WINCONx_HAWSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
465
	case DRM_FORMAT_XRGB8888:
466 467 468 469
		val |= WINCON0_BPPMODE_24BPP_888;
		val |= WINCONx_WSWP;
		val |= WINCONx_BURSTLEN_16WORD;
		break;
470 471
	case DRM_FORMAT_ARGB8888:
		val |= WINCON1_BPPMODE_25BPP_A1888
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
			| 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));
}

490
static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
491 492 493 494 495 496 497 498 499 500 501 502
{
	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));
}

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
/**
 * 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);
}

530
static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
531
{
532
	struct fimd_context *ctx = mgr->ctx;
533
	struct fimd_win_data *win_data;
534
	int win = zpos;
535
	unsigned long val, alpha, size;
536 537
	unsigned int last_x;
	unsigned int last_y;
538

I
Inki Dae 已提交
539 540 541
	if (ctx->suspended)
		return;

542 543 544
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

545
	if (win < 0 || win >= WINDOWS_NR)
546 547 548 549 550
		return;

	win_data = &ctx->win_data[win];

	/*
551
	 * SHADOWCON/PRTCON register is used for enabling timing.
552 553 554 555 556 557 558 559 560
	 *
	 * 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 */
561
	fimd_shadow_protect_win(ctx, win, true);
562 563

	/* buffer start address */
I
Inki Dae 已提交
564
	val = (unsigned long)win_data->dma_addr;
565 566 567
	writel(val, ctx->regs + VIDWx_BUF_START(win, 0));

	/* buffer end address */
568
	size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
I
Inki Dae 已提交
569
	val = (unsigned long)(win_data->dma_addr + size);
570 571 572
	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 已提交
573
			(unsigned long)win_data->dma_addr, val, size);
574 575
	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
			win_data->ovl_width, win_data->ovl_height);
576 577 578

	/* buffer size */
	val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
579 580 581
		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);
582 583 584 585
	writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));

	/* OSD position */
	val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
586 587 588
		VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
		VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
		VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
589 590
	writel(val, ctx->regs + VIDOSD_A(win));

591 592 593 594 595 596 597
	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--;

598 599 600
	val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
		VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);

601 602
	writel(val, ctx->regs + VIDOSD_B(win));

603
	DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
604
			win_data->offset_x, win_data->offset_y, last_x, last_y);
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619

	/* 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)
620
			offset = VIDOSD_C(win);
621
		val = win_data->ovl_width * win_data->ovl_height;
622 623 624 625 626
		writel(val, ctx->regs + offset);

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

627
	fimd_win_set_pixfmt(ctx, win);
628 629 630

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

633 634 635 636 637
	/* wincon */
	val = readl(ctx->regs + WINCON(win));
	val |= WINCONx_ENWIN;
	writel(val, ctx->regs + WINCON(win));

638
	/* Enable DMA channel and unprotect windows */
639 640 641 642 643 644 645
	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);
	}
646 647

	win_data->enabled = true;
648 649
}

650
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
651
{
652
	struct fimd_context *ctx = mgr->ctx;
653
	struct fimd_win_data *win_data;
654
	int win = zpos;
655 656
	u32 val;

657 658 659
	if (win == DEFAULT_ZPOS)
		win = ctx->default_win;

660
	if (win < 0 || win >= WINDOWS_NR)
661 662
		return;

663 664
	win_data = &ctx->win_data[win];

665 666 667 668 669 670
	if (ctx->suspended) {
		/* do not resume this window*/
		win_data->resume = false;
		return;
	}

671
	/* protect windows */
672
	fimd_shadow_protect_win(ctx, win, true);
673 674 675 676 677 678 679

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

	/* unprotect windows */
680 681 682 683 684 685 686
	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);
687 688

	win_data->enabled = false;
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
static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
{
	struct fimd_context *ctx = mgr->ctx;

	DRM_DEBUG_KMS("%d\n", mode);

	switch (mode) {
	case DRM_MODE_DPMS_ON:
		/*
		 * enable fimd hardware only if suspended status.
		 *
		 * P.S. fimd_dpms function would be called at booting time so
		 * clk_enable could be called double time.
		 */
		if (ctx->suspended)
			pm_runtime_get_sync(ctx->dev);
		break;
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
		if (!ctx->suspended)
			pm_runtime_put_sync(ctx->dev);
		break;
	default:
		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
		break;
	}
}

720
static struct exynos_drm_manager_ops fimd_manager_ops = {
721
	.initialize = fimd_mgr_initialize,
722
	.remove = fimd_mgr_remove,
723
	.dpms = fimd_dpms,
724 725
	.mode_fixup = fimd_mode_fixup,
	.mode_set = fimd_mode_set,
726 727 728 729 730 731 732
	.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,
733 734
};

735
static struct exynos_drm_manager fimd_manager = {
736 737
	.type = EXYNOS_DISPLAY_TYPE_LCD,
	.ops = &fimd_manager_ops,
738 739
};

740 741 742 743 744 745 746 747 748 749 750
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);

751
	/* check the crtc is detached already from encoder */
752
	if (ctx->pipe < 0 || !ctx->drm_dev)
753
		goto out;
I
Inki Dae 已提交
754

755 756
	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
	exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
757

758 759 760
	/* set wait vsync event to zero and wake up queue. */
	if (atomic_read(&ctx->wait_vsync_event)) {
		atomic_set(&ctx->wait_vsync_event, 0);
761
		wake_up(&ctx->wait_vsync_queue);
762
	}
763
out:
764 765 766 767 768 769 770 771 772 773 774 775 776
	return IRQ_HANDLED;
}

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));

777
	fimd_shadow_protect_win(ctx, win, false);
778 779
}

780
static int fimd_clock(struct fimd_context *ctx, bool enable)
781 782 783 784
{
	if (enable) {
		int ret;

V
Vikas Sajjan 已提交
785
		ret = clk_prepare_enable(ctx->bus_clk);
786 787 788
		if (ret < 0)
			return ret;

V
Vikas Sajjan 已提交
789
		ret = clk_prepare_enable(ctx->lcd_clk);
790
		if  (ret < 0) {
V
Vikas Sajjan 已提交
791
			clk_disable_unprepare(ctx->bus_clk);
792 793
			return ret;
		}
794
	} else {
V
Vikas Sajjan 已提交
795 796
		clk_disable_unprepare(ctx->lcd_clk);
		clk_disable_unprepare(ctx->bus_clk);
797 798 799 800 801
	}

	return 0;
}

802
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
803
{
804
	struct fimd_context *ctx = mgr->ctx;
805 806 807 808 809 810
	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;
811
		fimd_win_disable(mgr, i);
812
	}
813
	fimd_wait_for_vblank(mgr);
814 815
}

816
static void fimd_window_resume(struct exynos_drm_manager *mgr)
817
{
818
	struct fimd_context *ctx = mgr->ctx;
819 820 821 822 823 824 825 826 827 828
	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;
	}
}

829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
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);
}

844
static int fimd_activate(struct exynos_drm_manager *mgr, bool enable)
845
{
846 847
	struct fimd_context *ctx = mgr->ctx;

848 849 850 851 852 853
	if (enable) {
		int ret;

		ret = fimd_clock(ctx, true);
		if (ret < 0)
			return ret;
854 855 856 857 858

		ctx->suspended = false;

		/* if vblank was enabled status, enable it again. */
		if (test_and_clear_bit(0, &ctx->irq_flags))
859
			fimd_enable_vblank(mgr);
860

861
		fimd_window_resume(mgr);
862 863

		fimd_apply(mgr);
864
	} else {
865
		fimd_window_suspend(mgr);
866

867
		fimd_clock(ctx, false);
868 869 870 871 872 873
		ctx->suspended = true;
	}

	return 0;
}

874
static int fimd_get_platform_data(struct fimd_context *ctx, struct device *dev)
875
{
876 877
	struct videomode *vm;
	int ret;
878

879 880 881 882 883
	vm = &ctx->panel.vm;
	ret = of_get_videomode(dev->of_node, vm, OF_USE_NATIVE_MODE);
	if (ret) {
		DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
		return ret;
884 885
	}

886 887 888 889 890 891 892 893 894
	if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW)
		ctx->vidcon1 |= VIDCON1_INV_VSYNC;
	if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW)
		ctx->vidcon1 |= VIDCON1_INV_HSYNC;
	if (vm->flags & DISPLAY_FLAGS_DE_LOW)
		ctx->vidcon1 |= VIDCON1_INV_VDEN;
	if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
		ctx->vidcon1 |= VIDCON1_INV_VCLK;

895 896 897 898 899 900 901 902 903 904
	return 0;
}

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;
905

906 907 908
	if (!dev->of_node)
		return -ENODEV;

909
	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
910 911 912
	if (!ctx)
		return -ENOMEM;

913 914
	ctx->dev = dev;

915 916 917 918
	ret = fimd_get_platform_data(ctx, dev);
	if (ret)
		return ret;

919 920 921 922 923 924 925 926 927 928 929
	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);
	}
930 931 932

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

933
	ctx->regs = devm_ioremap_resource(dev, res);
934 935
	if (IS_ERR(ctx->regs))
		return PTR_ERR(ctx->regs);
936

937
	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
938 939
	if (!res) {
		dev_err(dev, "irq request failed.\n");
940
		return -ENXIO;
941 942
	}

943
	ret = devm_request_irq(dev, res->start, fimd_irq_handler,
944 945
							0, "drm_fimd", ctx);
	if (ret) {
946
		dev_err(dev, "irq request failed.\n");
947
		return ret;
948 949
	}

950
	ctx->driver_data = drm_fimd_get_driver_data(pdev);
951
	init_waitqueue_head(&ctx->wait_vsync_queue);
952
	atomic_set(&ctx->wait_vsync_event, 0);
953

954
	platform_set_drvdata(pdev, &fimd_manager);
955

956 957 958 959 960 961
	fimd_manager.ctx = ctx;
	exynos_drm_manager_register(&fimd_manager);

	fimd_display.ctx = ctx;
	exynos_drm_display_register(&fimd_display);

962 963 964 965 966 967
	pm_runtime_enable(dev);
	pm_runtime_get_sync(dev);

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

968 969 970
	return 0;
}

971
static int fimd_remove(struct platform_device *pdev)
972
{
973
	struct device *dev = &pdev->dev;
974 975
	struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
	struct fimd_context *ctx = mgr->ctx;
976

977 978
	exynos_drm_display_unregister(&fimd_display);
	exynos_drm_manager_unregister(&fimd_manager);
979

980 981 982 983 984 985 986 987 988
	if (ctx->suspended)
		goto out;

	pm_runtime_set_suspended(dev);
	pm_runtime_put_sync(dev);

out:
	pm_runtime_disable(dev);

989 990 991
	return 0;
}

I
Inki Dae 已提交
992 993 994
#ifdef CONFIG_PM_SLEEP
static int fimd_suspend(struct device *dev)
{
995
	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
I
Inki Dae 已提交
996

997 998 999 1000 1001
	/*
	 * do not use pm_runtime_suspend(). if pm_runtime_suspend() is
	 * called here, an error would be returned by that interface
	 * because the usage_count of pm runtime is more than 1.
	 */
1002
	if (!pm_runtime_suspended(dev))
1003
		return fimd_activate(mgr, false);
1004 1005

	return 0;
I
Inki Dae 已提交
1006 1007 1008 1009
}

static int fimd_resume(struct device *dev)
{
1010
	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
I
Inki Dae 已提交
1011

1012 1013 1014 1015 1016
	/*
	 * if entered to sleep when lcd panel was on, the usage_count
	 * of pm runtime would still be 1 so in this case, fimd driver
	 * should be on directly not drawing on pm runtime interface.
	 */
1017 1018
	if (pm_runtime_suspended(dev))
		return 0;
1019

1020
	return fimd_activate(mgr, true);
I
Inki Dae 已提交
1021 1022 1023
}
#endif

1024 1025 1026
#ifdef CONFIG_PM_RUNTIME
static int fimd_runtime_suspend(struct device *dev)
{
1027
	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
1028

1029
	return fimd_activate(mgr, false);
1030 1031 1032 1033
}

static int fimd_runtime_resume(struct device *dev)
{
1034
	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
1035

1036
	return fimd_activate(mgr, true);
1037 1038 1039 1040
}
#endif

static const struct dev_pm_ops fimd_pm_ops = {
I
Inki Dae 已提交
1041
	SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
1042 1043 1044
	SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
};

1045
struct platform_driver fimd_driver = {
1046
	.probe		= fimd_probe,
1047
	.remove		= fimd_remove,
1048 1049 1050
	.driver		= {
		.name	= "exynos4-fb",
		.owner	= THIS_MODULE,
1051
		.pm	= &fimd_pm_ops,
1052
		.of_match_table = fimd_driver_dt_match,
1053 1054
	},
};