blob: 909e6478d7d55f2896a5717a798016bd2ea7792c [file] [log] [blame]
Inki Dae1c248b72011-10-04 19:19:01 +09001/* exynos_drm_fimd.c
2 *
3 * Copyright (C) 2011 Samsung Electronics Co.Ltd
4 * Authors:
5 * Joonyoung Shim <jy0922.shim@samsung.com>
6 * Inki Dae <inki.dae@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
David Howells760285e2012-10-02 18:01:07 +010014#include <drm/drmP.h>
Inki Dae1c248b72011-10-04 19:19:01 +090015
16#include <linux/kernel.h>
Inki Dae1c248b72011-10-04 19:19:01 +090017#include <linux/platform_device.h>
18#include <linux/clk.h>
Sachin Kamat3f1c7812013-08-14 16:38:01 +053019#include <linux/of.h>
Joonyoung Shimd636ead2012-12-14 15:48:25 +090020#include <linux/of_device.h>
Joonyoung Shimcb91f6a2011-12-09 16:52:11 +090021#include <linux/pm_runtime.h>
Inki Daef37cd5e2014-05-09 14:25:20 +090022#include <linux/component.h>
YoungJun Cho3854fab2014-07-17 18:01:21 +090023#include <linux/mfd/syscon.h>
24#include <linux/regmap.h>
Inki Dae1c248b72011-10-04 19:19:01 +090025
Vikas Sajjan7f4596f2013-03-07 12:15:21 +053026#include <video/of_display_timing.h>
Andrzej Hajda111e6052013-08-21 16:22:01 +020027#include <video/of_videomode.h>
Leela Krishna Amudala5a213a52012-08-08 09:44:49 +090028#include <video/samsung_fimd.h>
Inki Dae1c248b72011-10-04 19:19:01 +090029#include <drm/exynos_drm.h>
Inki Dae1c248b72011-10-04 19:19:01 +090030
31#include "exynos_drm_drv.h"
32#include "exynos_drm_fbdev.h"
33#include "exynos_drm_crtc.h"
Inki Daebcc5cd1c2012-10-19 17:16:36 +090034#include "exynos_drm_iommu.h"
Inki Dae1c248b72011-10-04 19:19:01 +090035
36/*
Sachin Kamatb8654b32013-09-19 10:39:44 +053037 * FIMD stands for Fully Interactive Mobile Display and
Inki Dae1c248b72011-10-04 19:19:01 +090038 * as a display controller, it transfers contents drawn on memory
39 * to a LCD Panel through Display Interfaces such as RGB or
40 * CPU Interface.
41 */
42
Andrzej Hajda111e6052013-08-21 16:22:01 +020043#define FIMD_DEFAULT_FRAMERATE 60
Rahul Sharma66367462014-05-07 16:55:22 +053044#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
Andrzej Hajda111e6052013-08-21 16:22:01 +020045
Inki Dae1c248b72011-10-04 19:19:01 +090046/* position control register for hardware window 0, 2 ~ 4.*/
47#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
48#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
Leela Krishna Amudala0f10cf12013-03-07 23:28:52 -050049/*
50 * size control register for hardware windows 0 and alpha control register
51 * for hardware windows 1 ~ 4
52 */
53#define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16)
54/* size control register for hardware windows 1 ~ 2. */
Inki Dae1c248b72011-10-04 19:19:01 +090055#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
56
57#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
58#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
59#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
60
61/* color key control register for hardware window 1 ~ 4. */
Leela Krishna Amudala0f10cf12013-03-07 23:28:52 -050062#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
Inki Dae1c248b72011-10-04 19:19:01 +090063/* color key value register for hardware window 1 ~ 4. */
Leela Krishna Amudala0f10cf12013-03-07 23:28:52 -050064#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
Inki Dae1c248b72011-10-04 19:19:01 +090065
YoungJun Cho3854fab2014-07-17 18:01:21 +090066/* I80 / RGB trigger control register */
67#define TRIGCON 0x1A4
68#define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
69#define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
70
71/* display mode change control register except exynos4 */
72#define VIDOUT_CON 0x000
73#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8)
74
75/* I80 interface control for main LDI register */
76#define I80IFCONFAx(x) (0x1B0 + (x) * 4)
77#define I80IFCONFBx(x) (0x1B8 + (x) * 4)
78#define LCD_CS_SETUP(x) ((x) << 16)
79#define LCD_WR_SETUP(x) ((x) << 12)
80#define LCD_WR_ACTIVE(x) ((x) << 8)
81#define LCD_WR_HOLD(x) ((x) << 4)
82#define I80IFEN_ENABLE (1 << 0)
83
Inki Dae1c248b72011-10-04 19:19:01 +090084/* FIMD has totally five hardware windows. */
85#define WINDOWS_NR 5
86
Sean Paulbb7704d2014-01-30 16:19:06 -050087#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev))
Inki Dae1c248b72011-10-04 19:19:01 +090088
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +053089struct fimd_driver_data {
90 unsigned int timing_base;
YoungJun Cho3854fab2014-07-17 18:01:21 +090091 unsigned int lcdblk_offset;
92 unsigned int lcdblk_vt_shift;
93 unsigned int lcdblk_bypass_shift;
Tomasz Figade7af102013-05-01 21:02:27 +020094
95 unsigned int has_shadowcon:1;
Tomasz Figa411d9ed2013-05-01 21:02:28 +020096 unsigned int has_clksel:1;
Inki Dae5cc46212013-08-20 14:28:56 +090097 unsigned int has_limited_fmt:1;
YoungJun Cho3854fab2014-07-17 18:01:21 +090098 unsigned int has_vidoutcon:1;
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +053099};
100
Tomasz Figa725ddea2013-05-01 21:02:29 +0200101static struct fimd_driver_data s3c64xx_fimd_driver_data = {
102 .timing_base = 0x0,
103 .has_clksel = 1,
Inki Dae5cc46212013-08-20 14:28:56 +0900104 .has_limited_fmt = 1,
Tomasz Figa725ddea2013-05-01 21:02:29 +0200105};
106
Inki Daed6ce7b52014-08-18 16:53:19 +0900107static struct fimd_driver_data exynos3_fimd_driver_data = {
108 .timing_base = 0x20000,
109 .lcdblk_offset = 0x210,
110 .lcdblk_bypass_shift = 1,
111 .has_shadowcon = 1,
112 .has_vidoutcon = 1,
113};
114
Sachin Kamat6ecf18f2012-11-19 15:22:54 +0530115static struct fimd_driver_data exynos4_fimd_driver_data = {
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530116 .timing_base = 0x0,
YoungJun Cho3854fab2014-07-17 18:01:21 +0900117 .lcdblk_offset = 0x210,
118 .lcdblk_vt_shift = 10,
119 .lcdblk_bypass_shift = 1,
Tomasz Figade7af102013-05-01 21:02:27 +0200120 .has_shadowcon = 1,
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530121};
122
Sachin Kamat6ecf18f2012-11-19 15:22:54 +0530123static struct fimd_driver_data exynos5_fimd_driver_data = {
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530124 .timing_base = 0x20000,
YoungJun Cho3854fab2014-07-17 18:01:21 +0900125 .lcdblk_offset = 0x214,
126 .lcdblk_vt_shift = 24,
127 .lcdblk_bypass_shift = 15,
Tomasz Figade7af102013-05-01 21:02:27 +0200128 .has_shadowcon = 1,
YoungJun Cho3854fab2014-07-17 18:01:21 +0900129 .has_vidoutcon = 1,
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530130};
131
Inki Dae1c248b72011-10-04 19:19:01 +0900132struct fimd_win_data {
133 unsigned int offset_x;
134 unsigned int offset_y;
Inki Dae19c8b832011-10-14 13:29:46 +0900135 unsigned int ovl_width;
136 unsigned int ovl_height;
137 unsigned int fb_width;
138 unsigned int fb_height;
Inki Dae1c248b72011-10-04 19:19:01 +0900139 unsigned int bpp;
Inki Daea4f38a82013-08-20 13:51:02 +0900140 unsigned int pixel_format;
Inki Dae2c871122011-11-12 15:23:32 +0900141 dma_addr_t dma_addr;
Inki Dae1c248b72011-10-04 19:19:01 +0900142 unsigned int buf_offsize;
143 unsigned int line_size; /* bytes */
Inki Daeec05da92011-12-06 11:06:54 +0900144 bool enabled;
Prathyush Kdb7e55a2012-12-06 20:16:06 +0530145 bool resume;
Inki Dae1c248b72011-10-04 19:19:01 +0900146};
147
148struct fimd_context {
Sean Paulbb7704d2014-01-30 16:19:06 -0500149 struct device *dev;
Sean Paul40c8ab42014-01-30 16:19:04 -0500150 struct drm_device *drm_dev;
Inki Dae1c248b72011-10-04 19:19:01 +0900151 struct clk *bus_clk;
152 struct clk *lcd_clk;
Inki Dae1c248b72011-10-04 19:19:01 +0900153 void __iomem *regs;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900154 struct regmap *sysreg;
Sean Paula968e722014-01-30 16:19:20 -0500155 struct drm_display_mode mode;
Inki Dae1c248b72011-10-04 19:19:01 +0900156 struct fimd_win_data win_data[WINDOWS_NR];
Inki Dae1c248b72011-10-04 19:19:01 +0900157 unsigned int default_win;
158 unsigned long irq_flags;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900159 u32 vidcon0;
Inki Dae1c248b72011-10-04 19:19:01 +0900160 u32 vidcon1;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900161 u32 vidout_con;
162 u32 i80ifcon;
163 bool i80_if;
Joonyoung Shimcb91f6a2011-12-09 16:52:11 +0900164 bool suspended;
Sean Paul080be03d2014-02-19 21:02:55 +0900165 int pipe;
Prathyush K01ce1132012-12-06 20:16:04 +0530166 wait_queue_head_t wait_vsync_queue;
167 atomic_t wait_vsync_event;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900168 atomic_t win_updated;
169 atomic_t triggering;
Inki Dae1c248b72011-10-04 19:19:01 +0900170
Andrzej Hajda562ad9f2013-08-21 16:22:03 +0200171 struct exynos_drm_panel_info panel;
Tomasz Figa18873462013-05-01 21:02:26 +0200172 struct fimd_driver_data *driver_data;
Andrzej Hajda000cc922014-04-03 16:26:00 +0200173 struct exynos_drm_display *display;
Inki Dae1c248b72011-10-04 19:19:01 +0900174};
175
Joonyoung Shimd636ead2012-12-14 15:48:25 +0900176static const struct of_device_id fimd_driver_dt_match[] = {
Tomasz Figa725ddea2013-05-01 21:02:29 +0200177 { .compatible = "samsung,s3c6400-fimd",
178 .data = &s3c64xx_fimd_driver_data },
Inki Daed6ce7b52014-08-18 16:53:19 +0900179 { .compatible = "samsung,exynos3250-fimd",
180 .data = &exynos3_fimd_driver_data },
Vikas Sajjan5830daf2013-02-27 16:02:58 +0530181 { .compatible = "samsung,exynos4210-fimd",
Joonyoung Shimd636ead2012-12-14 15:48:25 +0900182 .data = &exynos4_fimd_driver_data },
Vikas Sajjan5830daf2013-02-27 16:02:58 +0530183 { .compatible = "samsung,exynos5250-fimd",
Joonyoung Shimd636ead2012-12-14 15:48:25 +0900184 .data = &exynos5_fimd_driver_data },
185 {},
186};
Sjoerd Simons0262cee2014-07-30 11:28:31 +0900187MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
Joonyoung Shimd636ead2012-12-14 15:48:25 +0900188
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530189static inline struct fimd_driver_data *drm_fimd_get_driver_data(
190 struct platform_device *pdev)
191{
Joonyoung Shimd636ead2012-12-14 15:48:25 +0900192 const struct of_device_id *of_id =
193 of_match_device(fimd_driver_dt_match, &pdev->dev);
194
Sachin Kamat2d3f1732013-08-28 10:47:58 +0530195 return (struct fimd_driver_data *)of_id->data;
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530196}
197
Akshu Agrawalf13bdbd2014-04-28 21:26:39 +0900198static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
199{
200 struct fimd_context *ctx = mgr->ctx;
201
202 if (ctx->suspended)
203 return;
204
205 atomic_set(&ctx->wait_vsync_event, 1);
206
207 /*
208 * wait for FIMD to signal VSYNC interrupt or return after
209 * timeout which is set to 50ms (refresh rate of 20).
210 */
211 if (!wait_event_timeout(ctx->wait_vsync_queue,
212 !atomic_read(&ctx->wait_vsync_event),
213 HZ/20))
214 DRM_DEBUG_KMS("vblank wait timed out.\n");
215}
216
217
218static void fimd_clear_channel(struct exynos_drm_manager *mgr)
219{
220 struct fimd_context *ctx = mgr->ctx;
221 int win, ch_enabled = 0;
222
223 DRM_DEBUG_KMS("%s\n", __FILE__);
224
225 /* Check if any channel is enabled. */
226 for (win = 0; win < WINDOWS_NR; win++) {
227 u32 val = readl(ctx->regs + SHADOWCON);
228 if (val & SHADOWCON_CHx_ENABLE(win)) {
229 val &= ~SHADOWCON_CHx_ENABLE(win);
230 writel(val, ctx->regs + SHADOWCON);
231 ch_enabled = 1;
232 }
233 }
234
235 /* Wait for vsync, as disable channel takes effect at next vsync */
236 if (ch_enabled)
237 fimd_wait_for_vblank(mgr);
238}
239
Sean Paulbb7704d2014-01-30 16:19:06 -0500240static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
Inki Daef37cd5e2014-05-09 14:25:20 +0900241 struct drm_device *drm_dev)
Sean Paul40c8ab42014-01-30 16:19:04 -0500242{
Sean Paulbb7704d2014-01-30 16:19:06 -0500243 struct fimd_context *ctx = mgr->ctx;
Inki Daef37cd5e2014-05-09 14:25:20 +0900244 struct exynos_drm_private *priv;
245 priv = drm_dev->dev_private;
Sean Paul40c8ab42014-01-30 16:19:04 -0500246
Inki Daef37cd5e2014-05-09 14:25:20 +0900247 mgr->drm_dev = ctx->drm_dev = drm_dev;
248 mgr->pipe = ctx->pipe = priv->pipe++;
Sean Paul080be03d2014-02-19 21:02:55 +0900249
250 /*
251 * enable drm irq mode.
252 * - with irq_enabled = true, we can use the vblank feature.
253 *
254 * P.S. note that we wouldn't use drm irq handler but
255 * just specific driver own one instead because
256 * drm framework supports only one irq handler.
257 */
258 drm_dev->irq_enabled = true;
259
260 /*
261 * with vblank_disable_allowed = true, vblank interrupt will be disabled
262 * by drm timer once a current process gives up ownership of
263 * vblank event.(after drm_vblank_put function is called)
264 */
265 drm_dev->vblank_disable_allowed = true;
266
267 /* attach this sub driver to iommu mapping if supported. */
Akshu Agrawalf13bdbd2014-04-28 21:26:39 +0900268 if (is_drm_iommu_supported(ctx->drm_dev)) {
269 /*
270 * If any channel is already active, iommu will throw
271 * a PAGE FAULT when enabled. So clear any channel if enabled.
272 */
273 fimd_clear_channel(mgr);
Sean Paul080be03d2014-02-19 21:02:55 +0900274 drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
Akshu Agrawalf13bdbd2014-04-28 21:26:39 +0900275 }
Sean Paul40c8ab42014-01-30 16:19:04 -0500276
277 return 0;
278}
279
Sean Paul080be03d2014-02-19 21:02:55 +0900280static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
Inki Daeec05da92011-12-06 11:06:54 +0900281{
Sean Paulbb7704d2014-01-30 16:19:06 -0500282 struct fimd_context *ctx = mgr->ctx;
Inki Daec32b06e2011-12-16 21:49:03 +0900283
Sean Paul080be03d2014-02-19 21:02:55 +0900284 /* detach this sub driver from iommu mapping if supported. */
285 if (is_drm_iommu_supported(ctx->drm_dev))
286 drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
Inki Daeec05da92011-12-06 11:06:54 +0900287}
288
Sean Paula968e722014-01-30 16:19:20 -0500289static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
290 const struct drm_display_mode *mode)
291{
292 unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
293 u32 clkdiv;
294
YoungJun Cho3854fab2014-07-17 18:01:21 +0900295 if (ctx->i80_if) {
296 /*
297 * The frame done interrupt should be occurred prior to the
298 * next TE signal.
299 */
300 ideal_clk *= 2;
301 }
302
Sean Paula968e722014-01-30 16:19:20 -0500303 /* Find the clock divider value that gets us closest to ideal_clk */
304 clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
305
306 return (clkdiv < 0x100) ? clkdiv : 0xff;
307}
308
309static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
310 const struct drm_display_mode *mode,
311 struct drm_display_mode *adjusted_mode)
312{
313 if (adjusted_mode->vrefresh == 0)
314 adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
315
316 return true;
317}
318
319static void fimd_mode_set(struct exynos_drm_manager *mgr,
320 const struct drm_display_mode *in_mode)
321{
322 struct fimd_context *ctx = mgr->ctx;
323
324 drm_mode_copy(&ctx->mode, in_mode);
325}
326
Sean Paulbb7704d2014-01-30 16:19:06 -0500327static void fimd_commit(struct exynos_drm_manager *mgr)
Inki Dae1c248b72011-10-04 19:19:01 +0900328{
Sean Paulbb7704d2014-01-30 16:19:06 -0500329 struct fimd_context *ctx = mgr->ctx;
Sean Paula968e722014-01-30 16:19:20 -0500330 struct drm_display_mode *mode = &ctx->mode;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900331 struct fimd_driver_data *driver_data = ctx->driver_data;
332 void *timing_base = ctx->regs + driver_data->timing_base;
333 u32 val, clkdiv;
Inki Dae1c248b72011-10-04 19:19:01 +0900334
Inki Daee30d4bc2011-12-12 16:35:20 +0900335 if (ctx->suspended)
336 return;
337
Sean Paula968e722014-01-30 16:19:20 -0500338 /* nothing to do if we haven't set the mode yet */
339 if (mode->htotal == 0 || mode->vtotal == 0)
340 return;
341
YoungJun Cho3854fab2014-07-17 18:01:21 +0900342 if (ctx->i80_if) {
343 val = ctx->i80ifcon | I80IFEN_ENABLE;
344 writel(val, timing_base + I80IFCONFAx(0));
Inki Dae1c248b72011-10-04 19:19:01 +0900345
YoungJun Cho3854fab2014-07-17 18:01:21 +0900346 /* disable auto frame rate */
347 writel(0, timing_base + I80IFCONFBx(0));
Sean Paula968e722014-01-30 16:19:20 -0500348
YoungJun Cho3854fab2014-07-17 18:01:21 +0900349 /* set video type selection to I80 interface */
350 if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
351 driver_data->lcdblk_offset,
352 0x3 << driver_data->lcdblk_vt_shift,
353 0x1 << driver_data->lcdblk_vt_shift)) {
354 DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
355 return;
356 }
357 } else {
358 int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
359 u32 vidcon1;
Inki Dae1c248b72011-10-04 19:19:01 +0900360
YoungJun Cho3854fab2014-07-17 18:01:21 +0900361 /* setup polarity values */
362 vidcon1 = ctx->vidcon1;
363 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
364 vidcon1 |= VIDCON1_INV_VSYNC;
365 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
366 vidcon1 |= VIDCON1_INV_HSYNC;
367 writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
Sean Paula968e722014-01-30 16:19:20 -0500368
YoungJun Cho3854fab2014-07-17 18:01:21 +0900369 /* setup vertical timing values. */
370 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
371 vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
372 vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
373
374 val = VIDTCON0_VBPD(vbpd - 1) |
375 VIDTCON0_VFPD(vfpd - 1) |
376 VIDTCON0_VSPW(vsync_len - 1);
377 writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
378
379 /* setup horizontal timing values. */
380 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
381 hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
382 hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
383
384 val = VIDTCON1_HBPD(hbpd - 1) |
385 VIDTCON1_HFPD(hfpd - 1) |
386 VIDTCON1_HSPW(hsync_len - 1);
387 writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
388 }
389
390 if (driver_data->has_vidoutcon)
391 writel(ctx->vidout_con, timing_base + VIDOUT_CON);
392
393 /* set bypass selection */
394 if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
395 driver_data->lcdblk_offset,
396 0x1 << driver_data->lcdblk_bypass_shift,
397 0x1 << driver_data->lcdblk_bypass_shift)) {
398 DRM_ERROR("Failed to update sysreg for bypass setting.\n");
399 return;
400 }
Inki Dae1c248b72011-10-04 19:19:01 +0900401
402 /* setup horizontal and vertical display size. */
Sean Paula968e722014-01-30 16:19:20 -0500403 val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
404 VIDTCON2_HOZVAL(mode->hdisplay - 1) |
405 VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
406 VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
Leela Krishna Amudalae2e13382012-09-21 16:52:15 +0530407 writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
Inki Dae1c248b72011-10-04 19:19:01 +0900408
Inki Dae1c248b72011-10-04 19:19:01 +0900409 /*
410 * fields of register with prefix '_F' would be updated
411 * at vsync(same as dma start)
412 */
YoungJun Cho3854fab2014-07-17 18:01:21 +0900413 val = ctx->vidcon0;
414 val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
Andrzej Hajda1d531062014-03-20 17:09:00 +0900415
416 if (ctx->driver_data->has_clksel)
417 val |= VIDCON0_CLKSEL_LCD;
418
419 clkdiv = fimd_calc_clkdiv(ctx, mode);
420 if (clkdiv > 1)
421 val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
422
Inki Dae1c248b72011-10-04 19:19:01 +0900423 writel(val, ctx->regs + VIDCON0);
424}
425
Sean Paulbb7704d2014-01-30 16:19:06 -0500426static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
Inki Dae1c248b72011-10-04 19:19:01 +0900427{
Sean Paulbb7704d2014-01-30 16:19:06 -0500428 struct fimd_context *ctx = mgr->ctx;
Inki Dae1c248b72011-10-04 19:19:01 +0900429 u32 val;
430
Joonyoung Shimcb91f6a2011-12-09 16:52:11 +0900431 if (ctx->suspended)
432 return -EPERM;
433
Inki Dae1c248b72011-10-04 19:19:01 +0900434 if (!test_and_set_bit(0, &ctx->irq_flags)) {
435 val = readl(ctx->regs + VIDINTCON0);
436
437 val |= VIDINTCON0_INT_ENABLE;
438 val |= VIDINTCON0_INT_FRAME;
439
440 val &= ~VIDINTCON0_FRAMESEL0_MASK;
441 val |= VIDINTCON0_FRAMESEL0_VSYNC;
442 val &= ~VIDINTCON0_FRAMESEL1_MASK;
443 val |= VIDINTCON0_FRAMESEL1_NONE;
444
445 writel(val, ctx->regs + VIDINTCON0);
446 }
447
448 return 0;
449}
450
Sean Paulbb7704d2014-01-30 16:19:06 -0500451static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
Inki Dae1c248b72011-10-04 19:19:01 +0900452{
Sean Paulbb7704d2014-01-30 16:19:06 -0500453 struct fimd_context *ctx = mgr->ctx;
Inki Dae1c248b72011-10-04 19:19:01 +0900454 u32 val;
455
Joonyoung Shimcb91f6a2011-12-09 16:52:11 +0900456 if (ctx->suspended)
457 return;
458
Inki Dae1c248b72011-10-04 19:19:01 +0900459 if (test_and_clear_bit(0, &ctx->irq_flags)) {
460 val = readl(ctx->regs + VIDINTCON0);
461
462 val &= ~VIDINTCON0_INT_FRAME;
463 val &= ~VIDINTCON0_INT_ENABLE;
464
465 writel(val, ctx->regs + VIDINTCON0);
466 }
467}
468
Sean Paulbb7704d2014-01-30 16:19:06 -0500469static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
470 struct exynos_drm_overlay *overlay)
Inki Dae1c248b72011-10-04 19:19:01 +0900471{
Sean Paulbb7704d2014-01-30 16:19:06 -0500472 struct fimd_context *ctx = mgr->ctx;
Inki Dae1c248b72011-10-04 19:19:01 +0900473 struct fimd_win_data *win_data;
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900474 int win;
Inki Dae19c8b832011-10-14 13:29:46 +0900475 unsigned long offset;
Inki Dae1c248b72011-10-04 19:19:01 +0900476
Inki Dae1c248b72011-10-04 19:19:01 +0900477 if (!overlay) {
Sean Paulbb7704d2014-01-30 16:19:06 -0500478 DRM_ERROR("overlay is NULL\n");
Inki Dae1c248b72011-10-04 19:19:01 +0900479 return;
480 }
481
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900482 win = overlay->zpos;
483 if (win == DEFAULT_ZPOS)
484 win = ctx->default_win;
485
Krzysztof Kozlowski37b006e2013-05-27 11:56:26 +0200486 if (win < 0 || win >= WINDOWS_NR)
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900487 return;
488
Inki Dae19c8b832011-10-14 13:29:46 +0900489 offset = overlay->fb_x * (overlay->bpp >> 3);
490 offset += overlay->fb_y * overlay->pitch;
491
492 DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
493
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900494 win_data = &ctx->win_data[win];
Inki Dae1c248b72011-10-04 19:19:01 +0900495
Inki Dae19c8b832011-10-14 13:29:46 +0900496 win_data->offset_x = overlay->crtc_x;
497 win_data->offset_y = overlay->crtc_y;
498 win_data->ovl_width = overlay->crtc_width;
499 win_data->ovl_height = overlay->crtc_height;
500 win_data->fb_width = overlay->fb_width;
501 win_data->fb_height = overlay->fb_height;
Seung-Woo Kim229d3532011-12-15 14:36:22 +0900502 win_data->dma_addr = overlay->dma_addr[0] + offset;
Inki Dae1c248b72011-10-04 19:19:01 +0900503 win_data->bpp = overlay->bpp;
Inki Daea4f38a82013-08-20 13:51:02 +0900504 win_data->pixel_format = overlay->pixel_format;
Inki Dae19c8b832011-10-14 13:29:46 +0900505 win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
506 (overlay->bpp >> 3);
507 win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
508
509 DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
510 win_data->offset_x, win_data->offset_y);
511 DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
512 win_data->ovl_width, win_data->ovl_height);
YoungJun Choddd8e952012-12-10 15:44:58 +0900513 DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
Inki Dae19c8b832011-10-14 13:29:46 +0900514 DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
515 overlay->fb_width, overlay->crtc_width);
Inki Dae1c248b72011-10-04 19:19:01 +0900516}
517
Sean Paulbb7704d2014-01-30 16:19:06 -0500518static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
Inki Dae1c248b72011-10-04 19:19:01 +0900519{
Inki Dae1c248b72011-10-04 19:19:01 +0900520 struct fimd_win_data *win_data = &ctx->win_data[win];
521 unsigned long val;
522
Inki Dae1c248b72011-10-04 19:19:01 +0900523 val = WINCONx_ENWIN;
524
Inki Dae5cc46212013-08-20 14:28:56 +0900525 /*
526 * In case of s3c64xx, window 0 doesn't support alpha channel.
527 * So the request format is ARGB8888 then change it to XRGB8888.
528 */
529 if (ctx->driver_data->has_limited_fmt && !win) {
530 if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
531 win_data->pixel_format = DRM_FORMAT_XRGB8888;
532 }
533
Inki Daea4f38a82013-08-20 13:51:02 +0900534 switch (win_data->pixel_format) {
535 case DRM_FORMAT_C8:
Inki Dae1c248b72011-10-04 19:19:01 +0900536 val |= WINCON0_BPPMODE_8BPP_PALETTE;
537 val |= WINCONx_BURSTLEN_8WORD;
538 val |= WINCONx_BYTSWP;
539 break;
Inki Daea4f38a82013-08-20 13:51:02 +0900540 case DRM_FORMAT_XRGB1555:
541 val |= WINCON0_BPPMODE_16BPP_1555;
542 val |= WINCONx_HAWSWP;
543 val |= WINCONx_BURSTLEN_16WORD;
544 break;
545 case DRM_FORMAT_RGB565:
Inki Dae1c248b72011-10-04 19:19:01 +0900546 val |= WINCON0_BPPMODE_16BPP_565;
547 val |= WINCONx_HAWSWP;
548 val |= WINCONx_BURSTLEN_16WORD;
549 break;
Inki Daea4f38a82013-08-20 13:51:02 +0900550 case DRM_FORMAT_XRGB8888:
Inki Dae1c248b72011-10-04 19:19:01 +0900551 val |= WINCON0_BPPMODE_24BPP_888;
552 val |= WINCONx_WSWP;
553 val |= WINCONx_BURSTLEN_16WORD;
554 break;
Inki Daea4f38a82013-08-20 13:51:02 +0900555 case DRM_FORMAT_ARGB8888:
556 val |= WINCON1_BPPMODE_25BPP_A1888
Inki Dae1c248b72011-10-04 19:19:01 +0900557 | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
558 val |= WINCONx_WSWP;
559 val |= WINCONx_BURSTLEN_16WORD;
560 break;
561 default:
562 DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
563
564 val |= WINCON0_BPPMODE_24BPP_888;
565 val |= WINCONx_WSWP;
566 val |= WINCONx_BURSTLEN_16WORD;
567 break;
568 }
569
570 DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
571
Rahul Sharma66367462014-05-07 16:55:22 +0530572 /*
573 * In case of exynos, setting dma-burst to 16Word causes permanent
574 * tearing for very small buffers, e.g. cursor buffer. Burst Mode
575 * switching which is based on overlay size is not recommended as
576 * overlay size varies alot towards the end of the screen and rapid
577 * movement causes unstable DMA which results into iommu crash/tear.
578 */
579
580 if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
581 val &= ~WINCONx_BURSTLEN_MASK;
582 val |= WINCONx_BURSTLEN_4WORD;
583 }
584
Inki Dae1c248b72011-10-04 19:19:01 +0900585 writel(val, ctx->regs + WINCON(win));
586}
587
Sean Paulbb7704d2014-01-30 16:19:06 -0500588static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
Inki Dae1c248b72011-10-04 19:19:01 +0900589{
Inki Dae1c248b72011-10-04 19:19:01 +0900590 unsigned int keycon0 = 0, keycon1 = 0;
591
Inki Dae1c248b72011-10-04 19:19:01 +0900592 keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
593 WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
594
595 keycon1 = WxKEYCON1_COLVAL(0xffffffff);
596
597 writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
598 writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
599}
600
Tomasz Figade7af102013-05-01 21:02:27 +0200601/**
602 * shadow_protect_win() - disable updating values from shadow registers at vsync
603 *
604 * @win: window to protect registers for
605 * @protect: 1 to protect (disable updates)
606 */
607static void fimd_shadow_protect_win(struct fimd_context *ctx,
608 int win, bool protect)
609{
610 u32 reg, bits, val;
611
612 if (ctx->driver_data->has_shadowcon) {
613 reg = SHADOWCON;
614 bits = SHADOWCON_WINx_PROTECT(win);
615 } else {
616 reg = PRTCON;
617 bits = PRTCON_PROTECT;
618 }
619
620 val = readl(ctx->regs + reg);
621 if (protect)
622 val |= bits;
623 else
624 val &= ~bits;
625 writel(val, ctx->regs + reg);
626}
627
Sean Paulbb7704d2014-01-30 16:19:06 -0500628static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
Inki Dae1c248b72011-10-04 19:19:01 +0900629{
Sean Paulbb7704d2014-01-30 16:19:06 -0500630 struct fimd_context *ctx = mgr->ctx;
Inki Dae1c248b72011-10-04 19:19:01 +0900631 struct fimd_win_data *win_data;
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900632 int win = zpos;
Inki Dae1c248b72011-10-04 19:19:01 +0900633 unsigned long val, alpha, size;
Joonyoung Shimf56aad32012-12-14 15:48:23 +0900634 unsigned int last_x;
635 unsigned int last_y;
Inki Dae1c248b72011-10-04 19:19:01 +0900636
Inki Daee30d4bc2011-12-12 16:35:20 +0900637 if (ctx->suspended)
638 return;
639
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900640 if (win == DEFAULT_ZPOS)
641 win = ctx->default_win;
642
Krzysztof Kozlowski37b006e2013-05-27 11:56:26 +0200643 if (win < 0 || win >= WINDOWS_NR)
Inki Dae1c248b72011-10-04 19:19:01 +0900644 return;
645
646 win_data = &ctx->win_data[win];
647
Sean Paula43b9332014-01-30 16:19:26 -0500648 /* If suspended, enable this on resume */
649 if (ctx->suspended) {
650 win_data->resume = true;
651 return;
652 }
653
Inki Dae1c248b72011-10-04 19:19:01 +0900654 /*
Tomasz Figade7af102013-05-01 21:02:27 +0200655 * SHADOWCON/PRTCON register is used for enabling timing.
Inki Dae1c248b72011-10-04 19:19:01 +0900656 *
657 * for example, once only width value of a register is set,
658 * if the dma is started then fimd hardware could malfunction so
659 * with protect window setting, the register fields with prefix '_F'
660 * wouldn't be updated at vsync also but updated once unprotect window
661 * is set.
662 */
663
664 /* protect windows */
Tomasz Figade7af102013-05-01 21:02:27 +0200665 fimd_shadow_protect_win(ctx, win, true);
Inki Dae1c248b72011-10-04 19:19:01 +0900666
667 /* buffer start address */
Inki Dae2c871122011-11-12 15:23:32 +0900668 val = (unsigned long)win_data->dma_addr;
Inki Dae1c248b72011-10-04 19:19:01 +0900669 writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
670
671 /* buffer end address */
Inki Dae19c8b832011-10-14 13:29:46 +0900672 size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
Inki Dae2c871122011-11-12 15:23:32 +0900673 val = (unsigned long)(win_data->dma_addr + size);
Inki Dae1c248b72011-10-04 19:19:01 +0900674 writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
675
676 DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
Inki Dae2c871122011-11-12 15:23:32 +0900677 (unsigned long)win_data->dma_addr, val, size);
Inki Dae19c8b832011-10-14 13:29:46 +0900678 DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
679 win_data->ovl_width, win_data->ovl_height);
Inki Dae1c248b72011-10-04 19:19:01 +0900680
681 /* buffer size */
682 val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
Joonyoung Shimca555e52012-12-14 15:48:24 +0900683 VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
684 VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
685 VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
Inki Dae1c248b72011-10-04 19:19:01 +0900686 writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
687
688 /* OSD position */
689 val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
Joonyoung Shimca555e52012-12-14 15:48:24 +0900690 VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
691 VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
692 VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
Inki Dae1c248b72011-10-04 19:19:01 +0900693 writel(val, ctx->regs + VIDOSD_A(win));
694
Joonyoung Shimf56aad32012-12-14 15:48:23 +0900695 last_x = win_data->offset_x + win_data->ovl_width;
696 if (last_x)
697 last_x--;
698 last_y = win_data->offset_y + win_data->ovl_height;
699 if (last_y)
700 last_y--;
701
Joonyoung Shimca555e52012-12-14 15:48:24 +0900702 val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
703 VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
704
Inki Dae1c248b72011-10-04 19:19:01 +0900705 writel(val, ctx->regs + VIDOSD_B(win));
706
Inki Dae19c8b832011-10-14 13:29:46 +0900707 DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
Joonyoung Shimf56aad32012-12-14 15:48:23 +0900708 win_data->offset_x, win_data->offset_y, last_x, last_y);
Inki Dae1c248b72011-10-04 19:19:01 +0900709
710 /* hardware window 0 doesn't support alpha channel. */
711 if (win != 0) {
712 /* OSD alpha */
713 alpha = VIDISD14C_ALPHA1_R(0xf) |
714 VIDISD14C_ALPHA1_G(0xf) |
715 VIDISD14C_ALPHA1_B(0xf);
716
717 writel(alpha, ctx->regs + VIDOSD_C(win));
718 }
719
720 /* OSD size */
721 if (win != 3 && win != 4) {
722 u32 offset = VIDOSD_D(win);
723 if (win == 0)
Leela Krishna Amudala0f10cf12013-03-07 23:28:52 -0500724 offset = VIDOSD_C(win);
Inki Dae19c8b832011-10-14 13:29:46 +0900725 val = win_data->ovl_width * win_data->ovl_height;
Inki Dae1c248b72011-10-04 19:19:01 +0900726 writel(val, ctx->regs + offset);
727
728 DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
729 }
730
Sean Paulbb7704d2014-01-30 16:19:06 -0500731 fimd_win_set_pixfmt(ctx, win);
Inki Dae1c248b72011-10-04 19:19:01 +0900732
733 /* hardware window 0 doesn't support color key. */
734 if (win != 0)
Sean Paulbb7704d2014-01-30 16:19:06 -0500735 fimd_win_set_colkey(ctx, win);
Inki Dae1c248b72011-10-04 19:19:01 +0900736
Inki Daeec05da92011-12-06 11:06:54 +0900737 /* wincon */
738 val = readl(ctx->regs + WINCON(win));
739 val |= WINCONx_ENWIN;
740 writel(val, ctx->regs + WINCON(win));
741
Inki Dae1c248b72011-10-04 19:19:01 +0900742 /* Enable DMA channel and unprotect windows */
Tomasz Figade7af102013-05-01 21:02:27 +0200743 fimd_shadow_protect_win(ctx, win, false);
744
745 if (ctx->driver_data->has_shadowcon) {
746 val = readl(ctx->regs + SHADOWCON);
747 val |= SHADOWCON_CHx_ENABLE(win);
748 writel(val, ctx->regs + SHADOWCON);
749 }
Inki Daeec05da92011-12-06 11:06:54 +0900750
751 win_data->enabled = true;
YoungJun Cho3854fab2014-07-17 18:01:21 +0900752
753 if (ctx->i80_if)
754 atomic_set(&ctx->win_updated, 1);
Inki Dae1c248b72011-10-04 19:19:01 +0900755}
756
Sean Paulbb7704d2014-01-30 16:19:06 -0500757static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
Inki Dae1c248b72011-10-04 19:19:01 +0900758{
Sean Paulbb7704d2014-01-30 16:19:06 -0500759 struct fimd_context *ctx = mgr->ctx;
Inki Daeec05da92011-12-06 11:06:54 +0900760 struct fimd_win_data *win_data;
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900761 int win = zpos;
Inki Dae1c248b72011-10-04 19:19:01 +0900762 u32 val;
763
Joonyoung Shim864ee9e2011-12-08 17:54:07 +0900764 if (win == DEFAULT_ZPOS)
765 win = ctx->default_win;
766
Krzysztof Kozlowski37b006e2013-05-27 11:56:26 +0200767 if (win < 0 || win >= WINDOWS_NR)
Inki Dae1c248b72011-10-04 19:19:01 +0900768 return;
769
Inki Daeec05da92011-12-06 11:06:54 +0900770 win_data = &ctx->win_data[win];
771
Prathyush Kdb7e55a2012-12-06 20:16:06 +0530772 if (ctx->suspended) {
773 /* do not resume this window*/
774 win_data->resume = false;
775 return;
776 }
777
Inki Dae1c248b72011-10-04 19:19:01 +0900778 /* protect windows */
Tomasz Figade7af102013-05-01 21:02:27 +0200779 fimd_shadow_protect_win(ctx, win, true);
Inki Dae1c248b72011-10-04 19:19:01 +0900780
781 /* wincon */
782 val = readl(ctx->regs + WINCON(win));
783 val &= ~WINCONx_ENWIN;
784 writel(val, ctx->regs + WINCON(win));
785
786 /* unprotect windows */
Tomasz Figade7af102013-05-01 21:02:27 +0200787 if (ctx->driver_data->has_shadowcon) {
788 val = readl(ctx->regs + SHADOWCON);
789 val &= ~SHADOWCON_CHx_ENABLE(win);
790 writel(val, ctx->regs + SHADOWCON);
791 }
792
793 fimd_shadow_protect_win(ctx, win, false);
Inki Daeec05da92011-12-06 11:06:54 +0900794
795 win_data->enabled = false;
Inki Dae1c248b72011-10-04 19:19:01 +0900796}
797
Sean Paula43b9332014-01-30 16:19:26 -0500798static void fimd_window_suspend(struct exynos_drm_manager *mgr)
799{
800 struct fimd_context *ctx = mgr->ctx;
801 struct fimd_win_data *win_data;
802 int i;
803
804 for (i = 0; i < WINDOWS_NR; i++) {
805 win_data = &ctx->win_data[i];
806 win_data->resume = win_data->enabled;
807 if (win_data->enabled)
808 fimd_win_disable(mgr, i);
809 }
810 fimd_wait_for_vblank(mgr);
811}
812
813static void fimd_window_resume(struct exynos_drm_manager *mgr)
814{
815 struct fimd_context *ctx = mgr->ctx;
816 struct fimd_win_data *win_data;
817 int i;
818
819 for (i = 0; i < WINDOWS_NR; i++) {
820 win_data = &ctx->win_data[i];
821 win_data->enabled = win_data->resume;
822 win_data->resume = false;
823 }
824}
825
826static void fimd_apply(struct exynos_drm_manager *mgr)
827{
828 struct fimd_context *ctx = mgr->ctx;
829 struct fimd_win_data *win_data;
830 int i;
831
832 for (i = 0; i < WINDOWS_NR; i++) {
833 win_data = &ctx->win_data[i];
834 if (win_data->enabled)
835 fimd_win_commit(mgr, i);
Andrzej Hajdad9b68d82014-06-09 16:10:59 +0200836 else
837 fimd_win_disable(mgr, i);
Sean Paula43b9332014-01-30 16:19:26 -0500838 }
839
840 fimd_commit(mgr);
841}
842
843static int fimd_poweron(struct exynos_drm_manager *mgr)
844{
845 struct fimd_context *ctx = mgr->ctx;
846 int ret;
847
848 if (!ctx->suspended)
849 return 0;
850
851 ctx->suspended = false;
852
Sean Paulaf65c802014-01-30 16:19:27 -0500853 pm_runtime_get_sync(ctx->dev);
854
Sean Paula43b9332014-01-30 16:19:26 -0500855 ret = clk_prepare_enable(ctx->bus_clk);
856 if (ret < 0) {
857 DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
858 goto bus_clk_err;
859 }
860
861 ret = clk_prepare_enable(ctx->lcd_clk);
862 if (ret < 0) {
863 DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
864 goto lcd_clk_err;
865 }
866
867 /* if vblank was enabled status, enable it again. */
868 if (test_and_clear_bit(0, &ctx->irq_flags)) {
869 ret = fimd_enable_vblank(mgr);
870 if (ret) {
871 DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
872 goto enable_vblank_err;
873 }
874 }
875
876 fimd_window_resume(mgr);
877
878 fimd_apply(mgr);
879
880 return 0;
881
882enable_vblank_err:
883 clk_disable_unprepare(ctx->lcd_clk);
884lcd_clk_err:
885 clk_disable_unprepare(ctx->bus_clk);
886bus_clk_err:
887 ctx->suspended = true;
888 return ret;
889}
890
891static int fimd_poweroff(struct exynos_drm_manager *mgr)
892{
893 struct fimd_context *ctx = mgr->ctx;
894
895 if (ctx->suspended)
896 return 0;
897
898 /*
899 * We need to make sure that all windows are disabled before we
900 * suspend that connector. Otherwise we might try to scan from
901 * a destroyed buffer later.
902 */
903 fimd_window_suspend(mgr);
904
905 clk_disable_unprepare(ctx->lcd_clk);
906 clk_disable_unprepare(ctx->bus_clk);
907
Sean Paulaf65c802014-01-30 16:19:27 -0500908 pm_runtime_put_sync(ctx->dev);
909
Sean Paula43b9332014-01-30 16:19:26 -0500910 ctx->suspended = true;
911 return 0;
912}
913
Sean Paul080be03d2014-02-19 21:02:55 +0900914static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
915{
Sean Paulaf65c802014-01-30 16:19:27 -0500916 DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
Sean Paul080be03d2014-02-19 21:02:55 +0900917
Sean Paul080be03d2014-02-19 21:02:55 +0900918 switch (mode) {
919 case DRM_MODE_DPMS_ON:
Sean Paulaf65c802014-01-30 16:19:27 -0500920 fimd_poweron(mgr);
Sean Paul080be03d2014-02-19 21:02:55 +0900921 break;
922 case DRM_MODE_DPMS_STANDBY:
923 case DRM_MODE_DPMS_SUSPEND:
924 case DRM_MODE_DPMS_OFF:
Sean Paulaf65c802014-01-30 16:19:27 -0500925 fimd_poweroff(mgr);
Sean Paul080be03d2014-02-19 21:02:55 +0900926 break;
927 default:
928 DRM_DEBUG_KMS("unspecified mode %d\n", mode);
929 break;
930 }
Sean Paul080be03d2014-02-19 21:02:55 +0900931}
932
YoungJun Cho3854fab2014-07-17 18:01:21 +0900933static void fimd_trigger(struct device *dev)
934{
935 struct exynos_drm_manager *mgr = get_fimd_manager(dev);
936 struct fimd_context *ctx = mgr->ctx;
937 struct fimd_driver_data *driver_data = ctx->driver_data;
938 void *timing_base = ctx->regs + driver_data->timing_base;
939 u32 reg;
940
941 atomic_set(&ctx->triggering, 1);
942
943 reg = readl(ctx->regs + VIDINTCON0);
944 reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
945 VIDINTCON0_INT_SYSMAINCON);
946 writel(reg, ctx->regs + VIDINTCON0);
947
948 reg = readl(timing_base + TRIGCON);
949 reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
950 writel(reg, timing_base + TRIGCON);
951}
952
953static void fimd_te_handler(struct exynos_drm_manager *mgr)
954{
955 struct fimd_context *ctx = mgr->ctx;
956
957 /* Checks the crtc is detached already from encoder */
958 if (ctx->pipe < 0 || !ctx->drm_dev)
959 return;
960
961 /*
962 * Skips to trigger if in triggering state, because multiple triggering
963 * requests can cause panel reset.
964 */
965 if (atomic_read(&ctx->triggering))
966 return;
967
968 /*
969 * If there is a page flip request, triggers and handles the page flip
970 * event so that current fb can be updated into panel GRAM.
971 */
972 if (atomic_add_unless(&ctx->win_updated, -1, 0))
973 fimd_trigger(ctx->dev);
974
975 /* Wakes up vsync event queue */
976 if (atomic_read(&ctx->wait_vsync_event)) {
977 atomic_set(&ctx->wait_vsync_event, 0);
978 wake_up(&ctx->wait_vsync_queue);
979
980 if (!atomic_read(&ctx->triggering))
981 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
982 }
983}
984
Sean Paul1c6244c2014-01-30 16:19:02 -0500985static struct exynos_drm_manager_ops fimd_manager_ops = {
986 .dpms = fimd_dpms,
Sean Paula968e722014-01-30 16:19:20 -0500987 .mode_fixup = fimd_mode_fixup,
988 .mode_set = fimd_mode_set,
Sean Paul1c6244c2014-01-30 16:19:02 -0500989 .commit = fimd_commit,
990 .enable_vblank = fimd_enable_vblank,
991 .disable_vblank = fimd_disable_vblank,
992 .wait_for_vblank = fimd_wait_for_vblank,
993 .win_mode_set = fimd_win_mode_set,
994 .win_commit = fimd_win_commit,
995 .win_disable = fimd_win_disable,
YoungJun Cho3854fab2014-07-17 18:01:21 +0900996 .te_handler = fimd_te_handler,
Inki Dae1c248b72011-10-04 19:19:01 +0900997};
998
Joonyoung Shim677e84c2012-04-05 20:49:27 +0900999static struct exynos_drm_manager fimd_manager = {
Sean Paul080be03d2014-02-19 21:02:55 +09001000 .type = EXYNOS_DISPLAY_TYPE_LCD,
1001 .ops = &fimd_manager_ops,
Joonyoung Shim677e84c2012-04-05 20:49:27 +09001002};
1003
Inki Dae1c248b72011-10-04 19:19:01 +09001004static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
1005{
1006 struct fimd_context *ctx = (struct fimd_context *)dev_id;
YoungJun Cho3854fab2014-07-17 18:01:21 +09001007 u32 val, clear_bit;
Inki Dae1c248b72011-10-04 19:19:01 +09001008
1009 val = readl(ctx->regs + VIDINTCON1);
1010
YoungJun Cho3854fab2014-07-17 18:01:21 +09001011 clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
1012 if (val & clear_bit)
1013 writel(clear_bit, ctx->regs + VIDINTCON1);
Inki Dae1c248b72011-10-04 19:19:01 +09001014
Inki Daeec05da92011-12-06 11:06:54 +09001015 /* check the crtc is detached already from encoder */
Sean Paul080be03d2014-02-19 21:02:55 +09001016 if (ctx->pipe < 0 || !ctx->drm_dev)
Inki Daeec05da92011-12-06 11:06:54 +09001017 goto out;
Inki Dae483b88f2011-11-11 21:28:00 +09001018
YoungJun Cho3854fab2014-07-17 18:01:21 +09001019 if (ctx->i80_if) {
1020 /* unset I80 frame done interrupt */
1021 val = readl(ctx->regs + VIDINTCON0);
1022 val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
1023 writel(val, ctx->regs + VIDINTCON0);
Inki Dae1c248b72011-10-04 19:19:01 +09001024
YoungJun Cho3854fab2014-07-17 18:01:21 +09001025 /* exit triggering mode */
1026 atomic_set(&ctx->triggering, 0);
1027
1028 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
1029 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
1030 } else {
1031 drm_handle_vblank(ctx->drm_dev, ctx->pipe);
1032 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
1033
1034 /* set wait vsync event to zero and wake up queue. */
1035 if (atomic_read(&ctx->wait_vsync_event)) {
1036 atomic_set(&ctx->wait_vsync_event, 0);
1037 wake_up(&ctx->wait_vsync_queue);
1038 }
Prathyush K01ce1132012-12-06 20:16:04 +05301039 }
YoungJun Cho3854fab2014-07-17 18:01:21 +09001040
Inki Daeec05da92011-12-06 11:06:54 +09001041out:
Inki Dae1c248b72011-10-04 19:19:01 +09001042 return IRQ_HANDLED;
1043}
1044
Inki Daef37cd5e2014-05-09 14:25:20 +09001045static int fimd_bind(struct device *dev, struct device *master, void *data)
Andrzej Hajda562ad9f2013-08-21 16:22:03 +02001046{
Andrzej Hajda000cc922014-04-03 16:26:00 +02001047 struct fimd_context *ctx = fimd_manager.ctx;
Inki Daef37cd5e2014-05-09 14:25:20 +09001048 struct drm_device *drm_dev = data;
Andrzej Hajda000cc922014-04-03 16:26:00 +02001049
1050 fimd_mgr_initialize(&fimd_manager, drm_dev);
1051 exynos_drm_crtc_create(&fimd_manager);
1052 if (ctx->display)
1053 exynos_drm_create_enc_conn(drm_dev, ctx->display);
1054
Andrzej Hajda000cc922014-04-03 16:26:00 +02001055 return 0;
1056
1057}
1058
1059static void fimd_unbind(struct device *dev, struct device *master,
1060 void *data)
1061{
1062 struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
1063 struct fimd_context *ctx = fimd_manager.ctx;
1064 struct drm_crtc *crtc = mgr->crtc;
1065
1066 fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
1067
1068 if (ctx->display)
1069 exynos_dpi_remove(dev);
1070
1071 fimd_mgr_remove(mgr);
1072
1073 crtc->funcs->destroy(crtc);
1074}
1075
1076static const struct component_ops fimd_component_ops = {
1077 .bind = fimd_bind,
1078 .unbind = fimd_unbind,
1079};
1080
1081static int fimd_probe(struct platform_device *pdev)
1082{
1083 struct device *dev = &pdev->dev;
1084 struct fimd_context *ctx;
YoungJun Cho3854fab2014-07-17 18:01:21 +09001085 struct device_node *i80_if_timings;
Andrzej Hajda000cc922014-04-03 16:26:00 +02001086 struct resource *res;
Andrzej Hajda562ad9f2013-08-21 16:22:03 +02001087 int ret = -EINVAL;
Inki Dae1c248b72011-10-04 19:19:01 +09001088
Inki Daedf5225b2014-05-29 18:28:02 +09001089 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
1090 fimd_manager.type);
1091 if (ret)
1092 return ret;
1093
1094 if (!dev->of_node) {
1095 ret = -ENODEV;
1096 goto err_del_component;
1097 }
Sachin Kamat2d3f1732013-08-28 10:47:58 +05301098
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001099 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
Inki Daedf5225b2014-05-29 18:28:02 +09001100 if (!ctx) {
1101 ret = -ENOMEM;
1102 goto err_del_component;
1103 }
Inki Dae1c248b72011-10-04 19:19:01 +09001104
Sean Paulbb7704d2014-01-30 16:19:06 -05001105 ctx->dev = dev;
Sean Paula43b9332014-01-30 16:19:26 -05001106 ctx->suspended = true;
YoungJun Cho3854fab2014-07-17 18:01:21 +09001107 ctx->driver_data = drm_fimd_get_driver_data(pdev);
Sean Paulbb7704d2014-01-30 16:19:06 -05001108
Sean Paul1417f102014-01-30 16:19:23 -05001109 if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
1110 ctx->vidcon1 |= VIDCON1_INV_VDEN;
1111 if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
1112 ctx->vidcon1 |= VIDCON1_INV_VCLK;
Andrzej Hajda562ad9f2013-08-21 16:22:03 +02001113
YoungJun Cho3854fab2014-07-17 18:01:21 +09001114 i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
1115 if (i80_if_timings) {
1116 u32 val;
1117
1118 ctx->i80_if = true;
1119
1120 if (ctx->driver_data->has_vidoutcon)
1121 ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
1122 else
1123 ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
1124 /*
1125 * The user manual describes that this "DSI_EN" bit is required
1126 * to enable I80 24-bit data interface.
1127 */
1128 ctx->vidcon0 |= VIDCON0_DSI_EN;
1129
1130 if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
1131 val = 0;
1132 ctx->i80ifcon = LCD_CS_SETUP(val);
1133 if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
1134 val = 0;
1135 ctx->i80ifcon |= LCD_WR_SETUP(val);
1136 if (of_property_read_u32(i80_if_timings, "wr-active", &val))
1137 val = 1;
1138 ctx->i80ifcon |= LCD_WR_ACTIVE(val);
1139 if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
1140 val = 0;
1141 ctx->i80ifcon |= LCD_WR_HOLD(val);
1142 }
1143 of_node_put(i80_if_timings);
1144
1145 ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
1146 "samsung,sysreg");
1147 if (IS_ERR(ctx->sysreg)) {
1148 dev_warn(dev, "failed to get system register.\n");
1149 ctx->sysreg = NULL;
1150 }
1151
Sean Paula968e722014-01-30 16:19:20 -05001152 ctx->bus_clk = devm_clk_get(dev, "fimd");
1153 if (IS_ERR(ctx->bus_clk)) {
1154 dev_err(dev, "failed to get bus clock\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001155 ret = PTR_ERR(ctx->bus_clk);
1156 goto err_del_component;
Sean Paula968e722014-01-30 16:19:20 -05001157 }
1158
1159 ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
1160 if (IS_ERR(ctx->lcd_clk)) {
1161 dev_err(dev, "failed to get lcd clock\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001162 ret = PTR_ERR(ctx->lcd_clk);
1163 goto err_del_component;
Sean Paula968e722014-01-30 16:19:20 -05001164 }
Inki Dae1c248b72011-10-04 19:19:01 +09001165
Inki Dae1c248b72011-10-04 19:19:01 +09001166 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Inki Dae1c248b72011-10-04 19:19:01 +09001167
Seung-Woo Kimd873ab92013-05-22 21:14:14 +09001168 ctx->regs = devm_ioremap_resource(dev, res);
Inki Daedf5225b2014-05-29 18:28:02 +09001169 if (IS_ERR(ctx->regs)) {
1170 ret = PTR_ERR(ctx->regs);
1171 goto err_del_component;
1172 }
Inki Dae1c248b72011-10-04 19:19:01 +09001173
YoungJun Cho3854fab2014-07-17 18:01:21 +09001174 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1175 ctx->i80_if ? "lcd_sys" : "vsync");
Inki Dae1c248b72011-10-04 19:19:01 +09001176 if (!res) {
1177 dev_err(dev, "irq request failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001178 ret = -ENXIO;
1179 goto err_del_component;
Inki Dae1c248b72011-10-04 19:19:01 +09001180 }
1181
Sean Paul055e0c02014-01-30 16:19:21 -05001182 ret = devm_request_irq(dev, res->start, fimd_irq_handler,
Sachin Kamatedc57262012-06-19 11:47:39 +05301183 0, "drm_fimd", ctx);
1184 if (ret) {
Inki Dae1c248b72011-10-04 19:19:01 +09001185 dev_err(dev, "irq request failed.\n");
Inki Daedf5225b2014-05-29 18:28:02 +09001186 goto err_del_component;
Inki Dae1c248b72011-10-04 19:19:01 +09001187 }
1188
Daniel Vetter57ed0f72013-12-11 11:34:43 +01001189 init_waitqueue_head(&ctx->wait_vsync_queue);
Prathyush K01ce1132012-12-06 20:16:04 +05301190 atomic_set(&ctx->wait_vsync_event, 0);
Inki Dae1c248b72011-10-04 19:19:01 +09001191
Sean Paulbb7704d2014-01-30 16:19:06 -05001192 platform_set_drvdata(pdev, &fimd_manager);
Inki Daec32b06e2011-12-16 21:49:03 +09001193
Sean Paul080be03d2014-02-19 21:02:55 +09001194 fimd_manager.ctx = ctx;
Sean Paul080be03d2014-02-19 21:02:55 +09001195
Andrzej Hajda000cc922014-04-03 16:26:00 +02001196 ctx->display = exynos_dpi_probe(dev);
1197 if (IS_ERR(ctx->display))
1198 return PTR_ERR(ctx->display);
Inki Daef37cd5e2014-05-09 14:25:20 +09001199
1200 pm_runtime_enable(&pdev->dev);
1201
Inki Daedf5225b2014-05-29 18:28:02 +09001202 ret = component_add(&pdev->dev, &fimd_component_ops);
1203 if (ret)
1204 goto err_disable_pm_runtime;
1205
1206 return ret;
1207
1208err_disable_pm_runtime:
1209 pm_runtime_disable(&pdev->dev);
1210
1211err_del_component:
1212 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1213 return ret;
Inki Daef37cd5e2014-05-09 14:25:20 +09001214}
1215
1216static int fimd_remove(struct platform_device *pdev)
1217{
Sean Paulaf65c802014-01-30 16:19:27 -05001218 pm_runtime_disable(&pdev->dev);
Joonyoung Shimcb91f6a2011-12-09 16:52:11 +09001219
Inki Daedf5225b2014-05-29 18:28:02 +09001220 component_del(&pdev->dev, &fimd_component_ops);
1221 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
1222
Inki Dae1c248b72011-10-04 19:19:01 +09001223 return 0;
1224}
1225
Joonyoung Shim132a5b92012-03-16 18:47:08 +09001226struct platform_driver fimd_driver = {
Inki Dae1c248b72011-10-04 19:19:01 +09001227 .probe = fimd_probe,
Greg Kroah-Hartman56550d92012-12-21 15:09:25 -08001228 .remove = fimd_remove,
Inki Dae1c248b72011-10-04 19:19:01 +09001229 .driver = {
1230 .name = "exynos4-fb",
1231 .owner = THIS_MODULE,
Sachin Kamat2d3f1732013-08-28 10:47:58 +05301232 .of_match_table = fimd_driver_dt_match,
Inki Dae1c248b72011-10-04 19:19:01 +09001233 },
1234};