exynos_drm_fimd.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /* 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. */
  14. #include <drm/drmP.h>
  15. #include <linux/kernel.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/clk.h>
  18. #include <linux/of.h>
  19. #include <linux/of_device.h>
  20. #include <linux/pm_runtime.h>
  21. #include <linux/component.h>
  22. #include <linux/mfd/syscon.h>
  23. #include <linux/regmap.h>
  24. #include <video/of_display_timing.h>
  25. #include <video/of_videomode.h>
  26. #include <video/samsung_fimd.h>
  27. #include <drm/exynos_drm.h>
  28. #include "exynos_drm_drv.h"
  29. #include "exynos_drm_fbdev.h"
  30. #include "exynos_drm_crtc.h"
  31. #include "exynos_drm_iommu.h"
  32. /*
  33. * FIMD stands for Fully Interactive Mobile Display and
  34. * as a display controller, it transfers contents drawn on memory
  35. * to a LCD Panel through Display Interfaces such as RGB or
  36. * CPU Interface.
  37. */
  38. #define FIMD_DEFAULT_FRAMERATE 60
  39. #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
  40. /* position control register for hardware window 0, 2 ~ 4.*/
  41. #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
  42. #define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
  43. /*
  44. * size control register for hardware windows 0 and alpha control register
  45. * for hardware windows 1 ~ 4
  46. */
  47. #define VIDOSD_C(win) (VIDOSD_BASE + 0x08 + (win) * 16)
  48. /* size control register for hardware windows 1 ~ 2. */
  49. #define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
  50. #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
  51. #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
  52. #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
  53. /* color key control register for hardware window 1 ~ 4. */
  54. #define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + ((x - 1) * 8))
  55. /* color key value register for hardware window 1 ~ 4. */
  56. #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
  57. /* I80 / RGB trigger control register */
  58. #define TRIGCON 0x1A4
  59. #define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
  60. #define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
  61. /* display mode change control register except exynos4 */
  62. #define VIDOUT_CON 0x000
  63. #define VIDOUT_CON_F_I80_LDI0 (0x2 << 8)
  64. /* I80 interface control for main LDI register */
  65. #define I80IFCONFAx(x) (0x1B0 + (x) * 4)
  66. #define I80IFCONFBx(x) (0x1B8 + (x) * 4)
  67. #define LCD_CS_SETUP(x) ((x) << 16)
  68. #define LCD_WR_SETUP(x) ((x) << 12)
  69. #define LCD_WR_ACTIVE(x) ((x) << 8)
  70. #define LCD_WR_HOLD(x) ((x) << 4)
  71. #define I80IFEN_ENABLE (1 << 0)
  72. /* FIMD has totally five hardware windows. */
  73. #define WINDOWS_NR 5
  74. struct fimd_driver_data {
  75. unsigned int timing_base;
  76. unsigned int lcdblk_offset;
  77. unsigned int lcdblk_vt_shift;
  78. unsigned int lcdblk_bypass_shift;
  79. unsigned int has_shadowcon:1;
  80. unsigned int has_clksel:1;
  81. unsigned int has_limited_fmt:1;
  82. unsigned int has_vidoutcon:1;
  83. unsigned int has_vtsel:1;
  84. };
  85. static struct fimd_driver_data s3c64xx_fimd_driver_data = {
  86. .timing_base = 0x0,
  87. .has_clksel = 1,
  88. .has_limited_fmt = 1,
  89. };
  90. static struct fimd_driver_data exynos3_fimd_driver_data = {
  91. .timing_base = 0x20000,
  92. .lcdblk_offset = 0x210,
  93. .lcdblk_bypass_shift = 1,
  94. .has_shadowcon = 1,
  95. .has_vidoutcon = 1,
  96. };
  97. static struct fimd_driver_data exynos4_fimd_driver_data = {
  98. .timing_base = 0x0,
  99. .lcdblk_offset = 0x210,
  100. .lcdblk_vt_shift = 10,
  101. .lcdblk_bypass_shift = 1,
  102. .has_shadowcon = 1,
  103. .has_vtsel = 1,
  104. };
  105. static struct fimd_driver_data exynos4415_fimd_driver_data = {
  106. .timing_base = 0x20000,
  107. .lcdblk_offset = 0x210,
  108. .lcdblk_vt_shift = 10,
  109. .lcdblk_bypass_shift = 1,
  110. .has_shadowcon = 1,
  111. .has_vidoutcon = 1,
  112. .has_vtsel = 1,
  113. };
  114. static struct fimd_driver_data exynos5_fimd_driver_data = {
  115. .timing_base = 0x20000,
  116. .lcdblk_offset = 0x214,
  117. .lcdblk_vt_shift = 24,
  118. .lcdblk_bypass_shift = 15,
  119. .has_shadowcon = 1,
  120. .has_vidoutcon = 1,
  121. .has_vtsel = 1,
  122. };
  123. struct fimd_win_data {
  124. unsigned int offset_x;
  125. unsigned int offset_y;
  126. unsigned int ovl_width;
  127. unsigned int ovl_height;
  128. unsigned int fb_width;
  129. unsigned int fb_height;
  130. unsigned int bpp;
  131. unsigned int pixel_format;
  132. dma_addr_t dma_addr;
  133. unsigned int buf_offsize;
  134. unsigned int line_size; /* bytes */
  135. bool enabled;
  136. bool resume;
  137. };
  138. struct fimd_context {
  139. struct exynos_drm_manager manager;
  140. struct device *dev;
  141. struct drm_device *drm_dev;
  142. struct clk *bus_clk;
  143. struct clk *lcd_clk;
  144. void __iomem *regs;
  145. struct regmap *sysreg;
  146. struct drm_display_mode mode;
  147. struct fimd_win_data win_data[WINDOWS_NR];
  148. unsigned int default_win;
  149. unsigned long irq_flags;
  150. u32 vidcon0;
  151. u32 vidcon1;
  152. u32 vidout_con;
  153. u32 i80ifcon;
  154. bool i80_if;
  155. bool suspended;
  156. int pipe;
  157. wait_queue_head_t wait_vsync_queue;
  158. atomic_t wait_vsync_event;
  159. atomic_t win_updated;
  160. atomic_t triggering;
  161. struct exynos_drm_panel_info panel;
  162. struct fimd_driver_data *driver_data;
  163. struct exynos_drm_display *display;
  164. };
  165. static inline struct fimd_context *mgr_to_fimd(struct exynos_drm_manager *mgr)
  166. {
  167. return container_of(mgr, struct fimd_context, manager);
  168. }
  169. static const struct of_device_id fimd_driver_dt_match[] = {
  170. { .compatible = "samsung,s3c6400-fimd",
  171. .data = &s3c64xx_fimd_driver_data },
  172. { .compatible = "samsung,exynos3250-fimd",
  173. .data = &exynos3_fimd_driver_data },
  174. { .compatible = "samsung,exynos4210-fimd",
  175. .data = &exynos4_fimd_driver_data },
  176. { .compatible = "samsung,exynos4415-fimd",
  177. .data = &exynos4415_fimd_driver_data },
  178. { .compatible = "samsung,exynos5250-fimd",
  179. .data = &exynos5_fimd_driver_data },
  180. {},
  181. };
  182. MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
  183. static inline struct fimd_driver_data *drm_fimd_get_driver_data(
  184. struct platform_device *pdev)
  185. {
  186. const struct of_device_id *of_id =
  187. of_match_device(fimd_driver_dt_match, &pdev->dev);
  188. return (struct fimd_driver_data *)of_id->data;
  189. }
  190. static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
  191. {
  192. struct fimd_context *ctx = mgr_to_fimd(mgr);
  193. if (ctx->suspended)
  194. return;
  195. atomic_set(&ctx->wait_vsync_event, 1);
  196. /*
  197. * wait for FIMD to signal VSYNC interrupt or return after
  198. * timeout which is set to 50ms (refresh rate of 20).
  199. */
  200. if (!wait_event_timeout(ctx->wait_vsync_queue,
  201. !atomic_read(&ctx->wait_vsync_event),
  202. HZ/20))
  203. DRM_DEBUG_KMS("vblank wait timed out.\n");
  204. }
  205. static void fimd_enable_video_output(struct fimd_context *ctx, int win,
  206. bool enable)
  207. {
  208. u32 val = readl(ctx->regs + WINCON(win));
  209. if (enable)
  210. val |= WINCONx_ENWIN;
  211. else
  212. val &= ~WINCONx_ENWIN;
  213. writel(val, ctx->regs + WINCON(win));
  214. }
  215. static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
  216. bool enable)
  217. {
  218. u32 val = readl(ctx->regs + SHADOWCON);
  219. if (enable)
  220. val |= SHADOWCON_CHx_ENABLE(win);
  221. else
  222. val &= ~SHADOWCON_CHx_ENABLE(win);
  223. writel(val, ctx->regs + SHADOWCON);
  224. }
  225. static void fimd_clear_channel(struct exynos_drm_manager *mgr)
  226. {
  227. struct fimd_context *ctx = mgr_to_fimd(mgr);
  228. int win, ch_enabled = 0;
  229. DRM_DEBUG_KMS("%s\n", __FILE__);
  230. /* Check if any channel is enabled. */
  231. for (win = 0; win < WINDOWS_NR; win++) {
  232. u32 val = readl(ctx->regs + WINCON(win));
  233. if (val & WINCONx_ENWIN) {
  234. fimd_enable_video_output(ctx, win, false);
  235. if (ctx->driver_data->has_shadowcon)
  236. fimd_enable_shadow_channel_path(ctx, win,
  237. false);
  238. ch_enabled = 1;
  239. }
  240. }
  241. /* Wait for vsync, as disable channel takes effect at next vsync */
  242. if (ch_enabled) {
  243. unsigned int state = ctx->suspended;
  244. ctx->suspended = 0;
  245. fimd_wait_for_vblank(mgr);
  246. ctx->suspended = state;
  247. }
  248. }
  249. static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
  250. struct drm_device *drm_dev)
  251. {
  252. struct fimd_context *ctx = mgr_to_fimd(mgr);
  253. struct exynos_drm_private *priv;
  254. priv = drm_dev->dev_private;
  255. mgr->drm_dev = ctx->drm_dev = drm_dev;
  256. mgr->pipe = ctx->pipe = priv->pipe++;
  257. /* attach this sub driver to iommu mapping if supported. */
  258. if (is_drm_iommu_supported(ctx->drm_dev)) {
  259. /*
  260. * If any channel is already active, iommu will throw
  261. * a PAGE FAULT when enabled. So clear any channel if enabled.
  262. */
  263. fimd_clear_channel(mgr);
  264. drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
  265. }
  266. return 0;
  267. }
  268. static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
  269. {
  270. struct fimd_context *ctx = mgr_to_fimd(mgr);
  271. /* detach this sub driver from iommu mapping if supported. */
  272. if (is_drm_iommu_supported(ctx->drm_dev))
  273. drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
  274. }
  275. static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
  276. const struct drm_display_mode *mode)
  277. {
  278. unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
  279. u32 clkdiv;
  280. if (ctx->i80_if) {
  281. /*
  282. * The frame done interrupt should be occurred prior to the
  283. * next TE signal.
  284. */
  285. ideal_clk *= 2;
  286. }
  287. /* Find the clock divider value that gets us closest to ideal_clk */
  288. clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
  289. return (clkdiv < 0x100) ? clkdiv : 0xff;
  290. }
  291. static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
  292. const struct drm_display_mode *mode,
  293. struct drm_display_mode *adjusted_mode)
  294. {
  295. if (adjusted_mode->vrefresh == 0)
  296. adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
  297. return true;
  298. }
  299. static void fimd_mode_set(struct exynos_drm_manager *mgr,
  300. const struct drm_display_mode *in_mode)
  301. {
  302. struct fimd_context *ctx = mgr_to_fimd(mgr);
  303. drm_mode_copy(&ctx->mode, in_mode);
  304. }
  305. static void fimd_commit(struct exynos_drm_manager *mgr)
  306. {
  307. struct fimd_context *ctx = mgr_to_fimd(mgr);
  308. struct drm_display_mode *mode = &ctx->mode;
  309. struct fimd_driver_data *driver_data = ctx->driver_data;
  310. void *timing_base = ctx->regs + driver_data->timing_base;
  311. u32 val, clkdiv;
  312. if (ctx->suspended)
  313. return;
  314. /* nothing to do if we haven't set the mode yet */
  315. if (mode->htotal == 0 || mode->vtotal == 0)
  316. return;
  317. if (ctx->i80_if) {
  318. val = ctx->i80ifcon | I80IFEN_ENABLE;
  319. writel(val, timing_base + I80IFCONFAx(0));
  320. /* disable auto frame rate */
  321. writel(0, timing_base + I80IFCONFBx(0));
  322. /* set video type selection to I80 interface */
  323. if (driver_data->has_vtsel && ctx->sysreg &&
  324. regmap_update_bits(ctx->sysreg,
  325. driver_data->lcdblk_offset,
  326. 0x3 << driver_data->lcdblk_vt_shift,
  327. 0x1 << driver_data->lcdblk_vt_shift)) {
  328. DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
  329. return;
  330. }
  331. } else {
  332. int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
  333. u32 vidcon1;
  334. /* setup polarity values */
  335. vidcon1 = ctx->vidcon1;
  336. if (mode->flags & DRM_MODE_FLAG_NVSYNC)
  337. vidcon1 |= VIDCON1_INV_VSYNC;
  338. if (mode->flags & DRM_MODE_FLAG_NHSYNC)
  339. vidcon1 |= VIDCON1_INV_HSYNC;
  340. writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
  341. /* setup vertical timing values. */
  342. vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
  343. vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
  344. vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
  345. val = VIDTCON0_VBPD(vbpd - 1) |
  346. VIDTCON0_VFPD(vfpd - 1) |
  347. VIDTCON0_VSPW(vsync_len - 1);
  348. writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
  349. /* setup horizontal timing values. */
  350. hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
  351. hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
  352. hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
  353. val = VIDTCON1_HBPD(hbpd - 1) |
  354. VIDTCON1_HFPD(hfpd - 1) |
  355. VIDTCON1_HSPW(hsync_len - 1);
  356. writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
  357. }
  358. if (driver_data->has_vidoutcon)
  359. writel(ctx->vidout_con, timing_base + VIDOUT_CON);
  360. /* set bypass selection */
  361. if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
  362. driver_data->lcdblk_offset,
  363. 0x1 << driver_data->lcdblk_bypass_shift,
  364. 0x1 << driver_data->lcdblk_bypass_shift)) {
  365. DRM_ERROR("Failed to update sysreg for bypass setting.\n");
  366. return;
  367. }
  368. /* setup horizontal and vertical display size. */
  369. val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
  370. VIDTCON2_HOZVAL(mode->hdisplay - 1) |
  371. VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
  372. VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
  373. writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
  374. /*
  375. * fields of register with prefix '_F' would be updated
  376. * at vsync(same as dma start)
  377. */
  378. val = ctx->vidcon0;
  379. val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
  380. if (ctx->driver_data->has_clksel)
  381. val |= VIDCON0_CLKSEL_LCD;
  382. clkdiv = fimd_calc_clkdiv(ctx, mode);
  383. if (clkdiv > 1)
  384. val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
  385. writel(val, ctx->regs + VIDCON0);
  386. }
  387. static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
  388. {
  389. struct fimd_context *ctx = mgr_to_fimd(mgr);
  390. u32 val;
  391. if (ctx->suspended)
  392. return -EPERM;
  393. if (!test_and_set_bit(0, &ctx->irq_flags)) {
  394. val = readl(ctx->regs + VIDINTCON0);
  395. val |= VIDINTCON0_INT_ENABLE;
  396. if (ctx->i80_if) {
  397. val |= VIDINTCON0_INT_I80IFDONE;
  398. val |= VIDINTCON0_INT_SYSMAINCON;
  399. val &= ~VIDINTCON0_INT_SYSSUBCON;
  400. } else {
  401. val |= VIDINTCON0_INT_FRAME;
  402. val &= ~VIDINTCON0_FRAMESEL0_MASK;
  403. val |= VIDINTCON0_FRAMESEL0_VSYNC;
  404. val &= ~VIDINTCON0_FRAMESEL1_MASK;
  405. val |= VIDINTCON0_FRAMESEL1_NONE;
  406. }
  407. writel(val, ctx->regs + VIDINTCON0);
  408. }
  409. return 0;
  410. }
  411. static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
  412. {
  413. struct fimd_context *ctx = mgr_to_fimd(mgr);
  414. u32 val;
  415. if (ctx->suspended)
  416. return;
  417. if (test_and_clear_bit(0, &ctx->irq_flags)) {
  418. val = readl(ctx->regs + VIDINTCON0);
  419. val &= ~VIDINTCON0_INT_ENABLE;
  420. if (ctx->i80_if) {
  421. val &= ~VIDINTCON0_INT_I80IFDONE;
  422. val &= ~VIDINTCON0_INT_SYSMAINCON;
  423. val &= ~VIDINTCON0_INT_SYSSUBCON;
  424. } else
  425. val &= ~VIDINTCON0_INT_FRAME;
  426. writel(val, ctx->regs + VIDINTCON0);
  427. }
  428. }
  429. static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
  430. struct exynos_drm_overlay *overlay)
  431. {
  432. struct fimd_context *ctx = mgr_to_fimd(mgr);
  433. struct fimd_win_data *win_data;
  434. int win;
  435. unsigned long offset;
  436. if (!overlay) {
  437. DRM_ERROR("overlay is NULL\n");
  438. return;
  439. }
  440. win = overlay->zpos;
  441. if (win == DEFAULT_ZPOS)
  442. win = ctx->default_win;
  443. if (win < 0 || win >= WINDOWS_NR)
  444. return;
  445. offset = overlay->fb_x * (overlay->bpp >> 3);
  446. offset += overlay->fb_y * overlay->pitch;
  447. DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
  448. win_data = &ctx->win_data[win];
  449. win_data->offset_x = overlay->crtc_x;
  450. win_data->offset_y = overlay->crtc_y;
  451. win_data->ovl_width = overlay->crtc_width;
  452. win_data->ovl_height = overlay->crtc_height;
  453. win_data->fb_width = overlay->fb_width;
  454. win_data->fb_height = overlay->fb_height;
  455. win_data->dma_addr = overlay->dma_addr[0] + offset;
  456. win_data->bpp = overlay->bpp;
  457. win_data->pixel_format = overlay->pixel_format;
  458. win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
  459. (overlay->bpp >> 3);
  460. win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
  461. DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
  462. win_data->offset_x, win_data->offset_y);
  463. DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
  464. win_data->ovl_width, win_data->ovl_height);
  465. DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr);
  466. DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
  467. overlay->fb_width, overlay->crtc_width);
  468. }
  469. static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
  470. {
  471. struct fimd_win_data *win_data = &ctx->win_data[win];
  472. unsigned long val;
  473. val = WINCONx_ENWIN;
  474. /*
  475. * In case of s3c64xx, window 0 doesn't support alpha channel.
  476. * So the request format is ARGB8888 then change it to XRGB8888.
  477. */
  478. if (ctx->driver_data->has_limited_fmt && !win) {
  479. if (win_data->pixel_format == DRM_FORMAT_ARGB8888)
  480. win_data->pixel_format = DRM_FORMAT_XRGB8888;
  481. }
  482. switch (win_data->pixel_format) {
  483. case DRM_FORMAT_C8:
  484. val |= WINCON0_BPPMODE_8BPP_PALETTE;
  485. val |= WINCONx_BURSTLEN_8WORD;
  486. val |= WINCONx_BYTSWP;
  487. break;
  488. case DRM_FORMAT_XRGB1555:
  489. val |= WINCON0_BPPMODE_16BPP_1555;
  490. val |= WINCONx_HAWSWP;
  491. val |= WINCONx_BURSTLEN_16WORD;
  492. break;
  493. case DRM_FORMAT_RGB565:
  494. val |= WINCON0_BPPMODE_16BPP_565;
  495. val |= WINCONx_HAWSWP;
  496. val |= WINCONx_BURSTLEN_16WORD;
  497. break;
  498. case DRM_FORMAT_XRGB8888:
  499. val |= WINCON0_BPPMODE_24BPP_888;
  500. val |= WINCONx_WSWP;
  501. val |= WINCONx_BURSTLEN_16WORD;
  502. break;
  503. case DRM_FORMAT_ARGB8888:
  504. val |= WINCON1_BPPMODE_25BPP_A1888
  505. | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
  506. val |= WINCONx_WSWP;
  507. val |= WINCONx_BURSTLEN_16WORD;
  508. break;
  509. default:
  510. DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
  511. val |= WINCON0_BPPMODE_24BPP_888;
  512. val |= WINCONx_WSWP;
  513. val |= WINCONx_BURSTLEN_16WORD;
  514. break;
  515. }
  516. DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
  517. /*
  518. * In case of exynos, setting dma-burst to 16Word causes permanent
  519. * tearing for very small buffers, e.g. cursor buffer. Burst Mode
  520. * switching which is based on overlay size is not recommended as
  521. * overlay size varies alot towards the end of the screen and rapid
  522. * movement causes unstable DMA which results into iommu crash/tear.
  523. */
  524. if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
  525. val &= ~WINCONx_BURSTLEN_MASK;
  526. val |= WINCONx_BURSTLEN_4WORD;
  527. }
  528. writel(val, ctx->regs + WINCON(win));
  529. }
  530. static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
  531. {
  532. unsigned int keycon0 = 0, keycon1 = 0;
  533. keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
  534. WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
  535. keycon1 = WxKEYCON1_COLVAL(0xffffffff);
  536. writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
  537. writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
  538. }
  539. /**
  540. * shadow_protect_win() - disable updating values from shadow registers at vsync
  541. *
  542. * @win: window to protect registers for
  543. * @protect: 1 to protect (disable updates)
  544. */
  545. static void fimd_shadow_protect_win(struct fimd_context *ctx,
  546. int win, bool protect)
  547. {
  548. u32 reg, bits, val;
  549. if (ctx->driver_data->has_shadowcon) {
  550. reg = SHADOWCON;
  551. bits = SHADOWCON_WINx_PROTECT(win);
  552. } else {
  553. reg = PRTCON;
  554. bits = PRTCON_PROTECT;
  555. }
  556. val = readl(ctx->regs + reg);
  557. if (protect)
  558. val |= bits;
  559. else
  560. val &= ~bits;
  561. writel(val, ctx->regs + reg);
  562. }
  563. static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
  564. {
  565. struct fimd_context *ctx = mgr_to_fimd(mgr);
  566. struct fimd_win_data *win_data;
  567. int win = zpos;
  568. unsigned long val, alpha, size;
  569. unsigned int last_x;
  570. unsigned int last_y;
  571. if (ctx->suspended)
  572. return;
  573. if (win == DEFAULT_ZPOS)
  574. win = ctx->default_win;
  575. if (win < 0 || win >= WINDOWS_NR)
  576. return;
  577. win_data = &ctx->win_data[win];
  578. /* If suspended, enable this on resume */
  579. if (ctx->suspended) {
  580. win_data->resume = true;
  581. return;
  582. }
  583. /*
  584. * SHADOWCON/PRTCON register is used for enabling timing.
  585. *
  586. * for example, once only width value of a register is set,
  587. * if the dma is started then fimd hardware could malfunction so
  588. * with protect window setting, the register fields with prefix '_F'
  589. * wouldn't be updated at vsync also but updated once unprotect window
  590. * is set.
  591. */
  592. /* protect windows */
  593. fimd_shadow_protect_win(ctx, win, true);
  594. /* buffer start address */
  595. val = (unsigned long)win_data->dma_addr;
  596. writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
  597. /* buffer end address */
  598. size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
  599. val = (unsigned long)(win_data->dma_addr + size);
  600. writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
  601. DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
  602. (unsigned long)win_data->dma_addr, val, size);
  603. DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
  604. win_data->ovl_width, win_data->ovl_height);
  605. /* buffer size */
  606. val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
  607. VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size) |
  608. VIDW_BUF_SIZE_OFFSET_E(win_data->buf_offsize) |
  609. VIDW_BUF_SIZE_PAGEWIDTH_E(win_data->line_size);
  610. writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
  611. /* OSD position */
  612. val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
  613. VIDOSDxA_TOPLEFT_Y(win_data->offset_y) |
  614. VIDOSDxA_TOPLEFT_X_E(win_data->offset_x) |
  615. VIDOSDxA_TOPLEFT_Y_E(win_data->offset_y);
  616. writel(val, ctx->regs + VIDOSD_A(win));
  617. last_x = win_data->offset_x + win_data->ovl_width;
  618. if (last_x)
  619. last_x--;
  620. last_y = win_data->offset_y + win_data->ovl_height;
  621. if (last_y)
  622. last_y--;
  623. val = VIDOSDxB_BOTRIGHT_X(last_x) | VIDOSDxB_BOTRIGHT_Y(last_y) |
  624. VIDOSDxB_BOTRIGHT_X_E(last_x) | VIDOSDxB_BOTRIGHT_Y_E(last_y);
  625. writel(val, ctx->regs + VIDOSD_B(win));
  626. DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
  627. win_data->offset_x, win_data->offset_y, last_x, last_y);
  628. /* hardware window 0 doesn't support alpha channel. */
  629. if (win != 0) {
  630. /* OSD alpha */
  631. alpha = VIDISD14C_ALPHA1_R(0xf) |
  632. VIDISD14C_ALPHA1_G(0xf) |
  633. VIDISD14C_ALPHA1_B(0xf);
  634. writel(alpha, ctx->regs + VIDOSD_C(win));
  635. }
  636. /* OSD size */
  637. if (win != 3 && win != 4) {
  638. u32 offset = VIDOSD_D(win);
  639. if (win == 0)
  640. offset = VIDOSD_C(win);
  641. val = win_data->ovl_width * win_data->ovl_height;
  642. writel(val, ctx->regs + offset);
  643. DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
  644. }
  645. fimd_win_set_pixfmt(ctx, win);
  646. /* hardware window 0 doesn't support color key. */
  647. if (win != 0)
  648. fimd_win_set_colkey(ctx, win);
  649. fimd_enable_video_output(ctx, win, true);
  650. if (ctx->driver_data->has_shadowcon)
  651. fimd_enable_shadow_channel_path(ctx, win, true);
  652. /* Enable DMA channel and unprotect windows */
  653. fimd_shadow_protect_win(ctx, win, false);
  654. win_data->enabled = true;
  655. if (ctx->i80_if)
  656. atomic_set(&ctx->win_updated, 1);
  657. }
  658. static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
  659. {
  660. struct fimd_context *ctx = mgr_to_fimd(mgr);
  661. struct fimd_win_data *win_data;
  662. int win = zpos;
  663. if (win == DEFAULT_ZPOS)
  664. win = ctx->default_win;
  665. if (win < 0 || win >= WINDOWS_NR)
  666. return;
  667. win_data = &ctx->win_data[win];
  668. if (ctx->suspended) {
  669. /* do not resume this window*/
  670. win_data->resume = false;
  671. return;
  672. }
  673. /* protect windows */
  674. fimd_shadow_protect_win(ctx, win, true);
  675. fimd_enable_video_output(ctx, win, false);
  676. if (ctx->driver_data->has_shadowcon)
  677. fimd_enable_shadow_channel_path(ctx, win, false);
  678. /* unprotect windows */
  679. fimd_shadow_protect_win(ctx, win, false);
  680. win_data->enabled = false;
  681. }
  682. static void fimd_window_suspend(struct exynos_drm_manager *mgr)
  683. {
  684. struct fimd_context *ctx = mgr_to_fimd(mgr);
  685. struct fimd_win_data *win_data;
  686. int i;
  687. for (i = 0; i < WINDOWS_NR; i++) {
  688. win_data = &ctx->win_data[i];
  689. win_data->resume = win_data->enabled;
  690. if (win_data->enabled)
  691. fimd_win_disable(mgr, i);
  692. }
  693. }
  694. static void fimd_window_resume(struct exynos_drm_manager *mgr)
  695. {
  696. struct fimd_context *ctx = mgr_to_fimd(mgr);
  697. struct fimd_win_data *win_data;
  698. int i;
  699. for (i = 0; i < WINDOWS_NR; i++) {
  700. win_data = &ctx->win_data[i];
  701. win_data->enabled = win_data->resume;
  702. win_data->resume = false;
  703. }
  704. }
  705. static void fimd_apply(struct exynos_drm_manager *mgr)
  706. {
  707. struct fimd_context *ctx = mgr_to_fimd(mgr);
  708. struct fimd_win_data *win_data;
  709. int i;
  710. for (i = 0; i < WINDOWS_NR; i++) {
  711. win_data = &ctx->win_data[i];
  712. if (win_data->enabled)
  713. fimd_win_commit(mgr, i);
  714. else
  715. fimd_win_disable(mgr, i);
  716. }
  717. fimd_commit(mgr);
  718. }
  719. static int fimd_poweron(struct exynos_drm_manager *mgr)
  720. {
  721. struct fimd_context *ctx = mgr_to_fimd(mgr);
  722. int ret;
  723. if (!ctx->suspended)
  724. return 0;
  725. ctx->suspended = false;
  726. pm_runtime_get_sync(ctx->dev);
  727. ret = clk_prepare_enable(ctx->bus_clk);
  728. if (ret < 0) {
  729. DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
  730. goto bus_clk_err;
  731. }
  732. ret = clk_prepare_enable(ctx->lcd_clk);
  733. if (ret < 0) {
  734. DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
  735. goto lcd_clk_err;
  736. }
  737. /* if vblank was enabled status, enable it again. */
  738. if (test_and_clear_bit(0, &ctx->irq_flags)) {
  739. ret = fimd_enable_vblank(mgr);
  740. if (ret) {
  741. DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
  742. goto enable_vblank_err;
  743. }
  744. }
  745. fimd_window_resume(mgr);
  746. fimd_apply(mgr);
  747. return 0;
  748. enable_vblank_err:
  749. clk_disable_unprepare(ctx->lcd_clk);
  750. lcd_clk_err:
  751. clk_disable_unprepare(ctx->bus_clk);
  752. bus_clk_err:
  753. ctx->suspended = true;
  754. return ret;
  755. }
  756. static int fimd_poweroff(struct exynos_drm_manager *mgr)
  757. {
  758. struct fimd_context *ctx = mgr_to_fimd(mgr);
  759. if (ctx->suspended)
  760. return 0;
  761. /*
  762. * We need to make sure that all windows are disabled before we
  763. * suspend that connector. Otherwise we might try to scan from
  764. * a destroyed buffer later.
  765. */
  766. fimd_window_suspend(mgr);
  767. clk_disable_unprepare(ctx->lcd_clk);
  768. clk_disable_unprepare(ctx->bus_clk);
  769. pm_runtime_put_sync(ctx->dev);
  770. ctx->suspended = true;
  771. return 0;
  772. }
  773. static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
  774. {
  775. DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
  776. switch (mode) {
  777. case DRM_MODE_DPMS_ON:
  778. fimd_poweron(mgr);
  779. break;
  780. case DRM_MODE_DPMS_STANDBY:
  781. case DRM_MODE_DPMS_SUSPEND:
  782. case DRM_MODE_DPMS_OFF:
  783. fimd_poweroff(mgr);
  784. break;
  785. default:
  786. DRM_DEBUG_KMS("unspecified mode %d\n", mode);
  787. break;
  788. }
  789. }
  790. static void fimd_trigger(struct device *dev)
  791. {
  792. struct fimd_context *ctx = dev_get_drvdata(dev);
  793. struct fimd_driver_data *driver_data = ctx->driver_data;
  794. void *timing_base = ctx->regs + driver_data->timing_base;
  795. u32 reg;
  796. /*
  797. * Skips triggering if in triggering state, because multiple triggering
  798. * requests can cause panel reset.
  799. */
  800. if (atomic_read(&ctx->triggering))
  801. return;
  802. /* Enters triggering mode */
  803. atomic_set(&ctx->triggering, 1);
  804. reg = readl(timing_base + TRIGCON);
  805. reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
  806. writel(reg, timing_base + TRIGCON);
  807. /*
  808. * Exits triggering mode if vblank is not enabled yet, because when the
  809. * VIDINTCON0 register is not set, it can not exit from triggering mode.
  810. */
  811. if (!test_bit(0, &ctx->irq_flags))
  812. atomic_set(&ctx->triggering, 0);
  813. }
  814. static void fimd_te_handler(struct exynos_drm_manager *mgr)
  815. {
  816. struct fimd_context *ctx = mgr_to_fimd(mgr);
  817. /* Checks the crtc is detached already from encoder */
  818. if (ctx->pipe < 0 || !ctx->drm_dev)
  819. return;
  820. /*
  821. * If there is a page flip request, triggers and handles the page flip
  822. * event so that current fb can be updated into panel GRAM.
  823. */
  824. if (atomic_add_unless(&ctx->win_updated, -1, 0))
  825. fimd_trigger(ctx->dev);
  826. /* Wakes up vsync event queue */
  827. if (atomic_read(&ctx->wait_vsync_event)) {
  828. atomic_set(&ctx->wait_vsync_event, 0);
  829. wake_up(&ctx->wait_vsync_queue);
  830. }
  831. if (test_bit(0, &ctx->irq_flags))
  832. drm_handle_vblank(ctx->drm_dev, ctx->pipe);
  833. }
  834. static struct exynos_drm_manager_ops fimd_manager_ops = {
  835. .dpms = fimd_dpms,
  836. .mode_fixup = fimd_mode_fixup,
  837. .mode_set = fimd_mode_set,
  838. .commit = fimd_commit,
  839. .enable_vblank = fimd_enable_vblank,
  840. .disable_vblank = fimd_disable_vblank,
  841. .wait_for_vblank = fimd_wait_for_vblank,
  842. .win_mode_set = fimd_win_mode_set,
  843. .win_commit = fimd_win_commit,
  844. .win_disable = fimd_win_disable,
  845. .te_handler = fimd_te_handler,
  846. };
  847. static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
  848. {
  849. struct fimd_context *ctx = (struct fimd_context *)dev_id;
  850. u32 val, clear_bit;
  851. val = readl(ctx->regs + VIDINTCON1);
  852. clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
  853. if (val & clear_bit)
  854. writel(clear_bit, ctx->regs + VIDINTCON1);
  855. /* check the crtc is detached already from encoder */
  856. if (ctx->pipe < 0 || !ctx->drm_dev)
  857. goto out;
  858. if (ctx->i80_if) {
  859. exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
  860. /* Exits triggering mode */
  861. atomic_set(&ctx->triggering, 0);
  862. } else {
  863. drm_handle_vblank(ctx->drm_dev, ctx->pipe);
  864. exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
  865. /* set wait vsync event to zero and wake up queue. */
  866. if (atomic_read(&ctx->wait_vsync_event)) {
  867. atomic_set(&ctx->wait_vsync_event, 0);
  868. wake_up(&ctx->wait_vsync_queue);
  869. }
  870. }
  871. out:
  872. return IRQ_HANDLED;
  873. }
  874. static int fimd_bind(struct device *dev, struct device *master, void *data)
  875. {
  876. struct fimd_context *ctx = dev_get_drvdata(dev);
  877. struct drm_device *drm_dev = data;
  878. fimd_mgr_initialize(&ctx->manager, drm_dev);
  879. exynos_drm_crtc_create(&ctx->manager);
  880. if (ctx->display)
  881. exynos_drm_create_enc_conn(drm_dev, ctx->display);
  882. return 0;
  883. }
  884. static void fimd_unbind(struct device *dev, struct device *master,
  885. void *data)
  886. {
  887. struct fimd_context *ctx = dev_get_drvdata(dev);
  888. fimd_dpms(&ctx->manager, DRM_MODE_DPMS_OFF);
  889. if (ctx->display)
  890. exynos_dpi_remove(ctx->display);
  891. fimd_mgr_remove(&ctx->manager);
  892. }
  893. static const struct component_ops fimd_component_ops = {
  894. .bind = fimd_bind,
  895. .unbind = fimd_unbind,
  896. };
  897. static int fimd_probe(struct platform_device *pdev)
  898. {
  899. struct device *dev = &pdev->dev;
  900. struct fimd_context *ctx;
  901. struct device_node *i80_if_timings;
  902. struct resource *res;
  903. int ret = -EINVAL;
  904. if (!dev->of_node)
  905. return -ENODEV;
  906. ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  907. if (!ctx)
  908. return -ENOMEM;
  909. ctx->manager.type = EXYNOS_DISPLAY_TYPE_LCD;
  910. ctx->manager.ops = &fimd_manager_ops;
  911. ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
  912. ctx->manager.type);
  913. if (ret)
  914. return ret;
  915. ctx->dev = dev;
  916. ctx->suspended = true;
  917. ctx->driver_data = drm_fimd_get_driver_data(pdev);
  918. if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
  919. ctx->vidcon1 |= VIDCON1_INV_VDEN;
  920. if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
  921. ctx->vidcon1 |= VIDCON1_INV_VCLK;
  922. i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
  923. if (i80_if_timings) {
  924. u32 val;
  925. ctx->i80_if = true;
  926. if (ctx->driver_data->has_vidoutcon)
  927. ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
  928. else
  929. ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
  930. /*
  931. * The user manual describes that this "DSI_EN" bit is required
  932. * to enable I80 24-bit data interface.
  933. */
  934. ctx->vidcon0 |= VIDCON0_DSI_EN;
  935. if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
  936. val = 0;
  937. ctx->i80ifcon = LCD_CS_SETUP(val);
  938. if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
  939. val = 0;
  940. ctx->i80ifcon |= LCD_WR_SETUP(val);
  941. if (of_property_read_u32(i80_if_timings, "wr-active", &val))
  942. val = 1;
  943. ctx->i80ifcon |= LCD_WR_ACTIVE(val);
  944. if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
  945. val = 0;
  946. ctx->i80ifcon |= LCD_WR_HOLD(val);
  947. }
  948. of_node_put(i80_if_timings);
  949. ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
  950. "samsung,sysreg");
  951. if (IS_ERR(ctx->sysreg)) {
  952. dev_warn(dev, "failed to get system register.\n");
  953. ctx->sysreg = NULL;
  954. }
  955. ctx->bus_clk = devm_clk_get(dev, "fimd");
  956. if (IS_ERR(ctx->bus_clk)) {
  957. dev_err(dev, "failed to get bus clock\n");
  958. ret = PTR_ERR(ctx->bus_clk);
  959. goto err_del_component;
  960. }
  961. ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
  962. if (IS_ERR(ctx->lcd_clk)) {
  963. dev_err(dev, "failed to get lcd clock\n");
  964. ret = PTR_ERR(ctx->lcd_clk);
  965. goto err_del_component;
  966. }
  967. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  968. ctx->regs = devm_ioremap_resource(dev, res);
  969. if (IS_ERR(ctx->regs)) {
  970. ret = PTR_ERR(ctx->regs);
  971. goto err_del_component;
  972. }
  973. res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
  974. ctx->i80_if ? "lcd_sys" : "vsync");
  975. if (!res) {
  976. dev_err(dev, "irq request failed.\n");
  977. ret = -ENXIO;
  978. goto err_del_component;
  979. }
  980. ret = devm_request_irq(dev, res->start, fimd_irq_handler,
  981. 0, "drm_fimd", ctx);
  982. if (ret) {
  983. dev_err(dev, "irq request failed.\n");
  984. goto err_del_component;
  985. }
  986. init_waitqueue_head(&ctx->wait_vsync_queue);
  987. atomic_set(&ctx->wait_vsync_event, 0);
  988. platform_set_drvdata(pdev, ctx);
  989. ctx->display = exynos_dpi_probe(dev);
  990. if (IS_ERR(ctx->display)) {
  991. ret = PTR_ERR(ctx->display);
  992. goto err_del_component;
  993. }
  994. pm_runtime_enable(dev);
  995. ret = component_add(dev, &fimd_component_ops);
  996. if (ret)
  997. goto err_disable_pm_runtime;
  998. return ret;
  999. err_disable_pm_runtime:
  1000. pm_runtime_disable(dev);
  1001. err_del_component:
  1002. exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
  1003. return ret;
  1004. }
  1005. static int fimd_remove(struct platform_device *pdev)
  1006. {
  1007. pm_runtime_disable(&pdev->dev);
  1008. component_del(&pdev->dev, &fimd_component_ops);
  1009. exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
  1010. return 0;
  1011. }
  1012. struct platform_driver fimd_driver = {
  1013. .probe = fimd_probe,
  1014. .remove = fimd_remove,
  1015. .driver = {
  1016. .name = "exynos4-fb",
  1017. .owner = THIS_MODULE,
  1018. .of_match_table = fimd_driver_dt_match,
  1019. },
  1020. };