mtk_drm_plane.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*
  2. * Copyright (c) 2015 MediaTek Inc.
  3. * Author: CK Hu <ck.hu@mediatek.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include <drm/drmP.h>
  15. #include <drm/drm_atomic.h>
  16. #include <drm/drm_atomic_helper.h>
  17. #include <drm/drm_plane_helper.h>
  18. #include <linux/mailbox_controller.h>
  19. #include "mtk_drm_crtc.h"
  20. #include "mtk_drm_ddp_comp.h"
  21. #include "mtk_drm_drv.h"
  22. #include "mtk_drm_fb.h"
  23. #include "mtk_drm_gem.h"
  24. #include "mtk_drm_plane.h"
  25. #include "cmdq-sec.h"
  26. #define MTK_DRM_PLANE_SCALING_MIN 16
  27. #define MTK_DRM_PLANE_SCALING_MAX (1 << 16)
  28. static const u32 formats[] = {
  29. DRM_FORMAT_C8, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
  30. DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_BGRX8888,
  31. DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGBA8888,
  32. DRM_FORMAT_BGR888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR565,
  33. DRM_FORMAT_RGB565, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU,
  34. DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_ABGR2101010,
  35. DRM_FORMAT_ABGRFP16,
  36. };
  37. unsigned int to_crtc_plane_index(unsigned int plane_index)
  38. {
  39. if ((plane_index >= 0) && (plane_index < OVL_LAYER_NR))
  40. return plane_index;
  41. else if (plane_index < (OVL_LAYER_NR + EXTERNAL_INPUT_LAYER_NR))
  42. return plane_index - OVL_LAYER_NR;
  43. else if (plane_index < MAX_PLANE_NR)
  44. return plane_index - OVL_LAYER_NR - EXTERNAL_INPUT_LAYER_NR;
  45. else
  46. return 0;
  47. }
  48. int mtk_get_format_bpp(uint32_t format)
  49. {
  50. switch (format) {
  51. case MTK_DRM_FORMAT_DIM:
  52. case DRM_FORMAT_C8:
  53. return 0;
  54. case DRM_FORMAT_RGB565:
  55. case DRM_FORMAT_BGR565:
  56. case DRM_FORMAT_YUYV:
  57. case DRM_FORMAT_YVYU:
  58. case DRM_FORMAT_UYVY:
  59. case DRM_FORMAT_VYUY:
  60. return 2;
  61. case DRM_FORMAT_RGB888:
  62. case DRM_FORMAT_BGR888:
  63. return 3;
  64. case DRM_FORMAT_XRGB8888:
  65. case DRM_FORMAT_XBGR8888:
  66. case DRM_FORMAT_RGBX8888:
  67. case DRM_FORMAT_BGRX8888:
  68. case DRM_FORMAT_ARGB8888:
  69. case DRM_FORMAT_ABGR8888:
  70. case DRM_FORMAT_RGBA8888:
  71. case DRM_FORMAT_BGRA8888:
  72. case DRM_FORMAT_XRGB2101010:
  73. case DRM_FORMAT_XBGR2101010:
  74. case DRM_FORMAT_RGBX1010102:
  75. case DRM_FORMAT_BGRX1010102:
  76. case DRM_FORMAT_ARGB2101010:
  77. case DRM_FORMAT_ABGR2101010:
  78. case DRM_FORMAT_RGBA1010102:
  79. case DRM_FORMAT_BGRA1010102:
  80. return 4;
  81. case DRM_FORMAT_YUV422:
  82. case DRM_FORMAT_YVU422:
  83. return 2;
  84. case DRM_FORMAT_YUV444:
  85. case DRM_FORMAT_YVU444:
  86. return 3;
  87. case DRM_FORMAT_ABGRFP16:
  88. return 8;
  89. default:
  90. return 4;
  91. }
  92. }
  93. char *mtk_get_format_name(uint32_t format)
  94. {
  95. switch (format) {
  96. case DRM_FORMAT_C8:
  97. return "C8";
  98. case DRM_FORMAT_XRGB8888:
  99. return "XRGB8888";
  100. case DRM_FORMAT_XBGR8888:
  101. return "XBGR8888";
  102. case DRM_FORMAT_ARGB8888:
  103. return "ARGB8888";
  104. case DRM_FORMAT_ABGR8888:
  105. return "ABGR8888";
  106. case DRM_FORMAT_BGRX8888:
  107. return "BGRX8888";
  108. case DRM_FORMAT_RGBX8888:
  109. return "RGBX8888";
  110. case DRM_FORMAT_BGRA8888:
  111. return "BGRA8888";
  112. case DRM_FORMAT_RGBA8888:
  113. return "RGBA8888";
  114. case DRM_FORMAT_BGR888:
  115. return "BGR888";
  116. case DRM_FORMAT_RGB888:
  117. return "RGB888";
  118. case DRM_FORMAT_BGR565:
  119. return "BGR565";
  120. case DRM_FORMAT_RGB565:
  121. return "RGB565";
  122. case DRM_FORMAT_YUYV:
  123. return "YUYV";
  124. case DRM_FORMAT_YVYU:
  125. return "YVYU";
  126. case DRM_FORMAT_UYVY:
  127. return "UYVY";
  128. case DRM_FORMAT_VYUY:
  129. return "VYUY";
  130. case DRM_FORMAT_ABGR2101010:
  131. return "ABGR2101010";
  132. case DRM_FORMAT_ABGRFP16:
  133. return "ABGRFP16";
  134. }
  135. return "fmt_unknown";
  136. }
  137. static struct mtk_drm_property mtk_plane_property[PLANE_PROP_MAX] = {
  138. {DRM_MODE_PROP_ATOMIC, "NEXT_BUFF_IDX", 0, UINT_MAX, 0},
  139. {DRM_MODE_PROP_ATOMIC, "LYE_BLOB_IDX", 0, UINT_MAX, 0},
  140. {DRM_MODE_PROP_ATOMIC, "PLANE_PROP_ALPHA_CON", 0, 0x1, 0x1},
  141. {DRM_MODE_PROP_ATOMIC, "PLANE_PROP_PLANE_ALPHA", 0, 0xFF, 0xFF},
  142. {DRM_MODE_PROP_ATOMIC, "DATASPACE", 0, INT_MAX, 0},
  143. {DRM_MODE_PROP_ATOMIC, "VPITCH", 0, UINT_MAX, 0},
  144. {DRM_MODE_PROP_ATOMIC, "COMPRESS", 0, UINT_MAX, 0},
  145. {DRM_MODE_PROP_ATOMIC, "DIM_COLOR", 0, UINT_MAX, 0},
  146. };
  147. static void mtk_plane_reset(struct drm_plane *plane)
  148. {
  149. struct mtk_plane_state *state;
  150. if (plane->state) {
  151. __drm_atomic_helper_plane_destroy_state(plane->state);
  152. state = to_mtk_plane_state(plane->state);
  153. memset(state, 0, sizeof(*state));
  154. } else {
  155. state = kzalloc(sizeof(*state), GFP_KERNEL);
  156. if (!state)
  157. return;
  158. plane->state = &state->base;
  159. }
  160. state->prop_val[PLANE_PROP_ALPHA_CON] = 0x1;
  161. state->prop_val[PLANE_PROP_PLANE_ALPHA] = 0xFF;
  162. state->base.plane = plane;
  163. state->pending.format = DRM_FORMAT_RGB565;
  164. }
  165. static struct drm_plane_state *
  166. mtk_plane_duplicate_state(struct drm_plane *plane)
  167. {
  168. struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
  169. struct mtk_plane_state *state;
  170. state = kzalloc(sizeof(*state), GFP_KERNEL);
  171. if (!state)
  172. return NULL;
  173. __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
  174. if (state->base.plane != plane)
  175. DDPAEE("%s:%d, invalid plane:(%p,%p)\n",
  176. __func__, __LINE__,
  177. state->base.plane, plane);
  178. state->prop_val[PLANE_PROP_ALPHA_CON] =
  179. old_state->prop_val[PLANE_PROP_ALPHA_CON];
  180. state->prop_val[PLANE_PROP_PLANE_ALPHA] =
  181. old_state->prop_val[PLANE_PROP_PLANE_ALPHA];
  182. state->pending = old_state->pending;
  183. state->comp_state = old_state->comp_state;
  184. state->crtc = old_state->crtc;
  185. return &state->base;
  186. }
  187. static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
  188. struct drm_plane_state *state)
  189. {
  190. struct mtk_plane_state *s;
  191. s = to_mtk_plane_state(state);
  192. __drm_atomic_helper_plane_destroy_state(state);
  193. kfree(s);
  194. }
  195. static int mtk_plane_atomic_set_property(struct drm_plane *plane,
  196. struct drm_plane_state *state,
  197. struct drm_property *property,
  198. uint64_t val)
  199. {
  200. struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
  201. struct mtk_plane_state *plane_state = to_mtk_plane_state(state);
  202. int ret = 0;
  203. int i;
  204. if (!mtk_plane) {
  205. DDPPR_ERR("%s:%d mtk plane is null\n", __func__, __LINE__);
  206. return -EINVAL;
  207. }
  208. for (i = 0; i < PLANE_PROP_MAX; i++) {
  209. if (mtk_plane->plane_property[i] == property) {
  210. plane_state->prop_val[i] = (unsigned int)val;
  211. DDPDBG("set property:%s %d\n", property->name,
  212. (unsigned int)val);
  213. return ret;
  214. }
  215. }
  216. DDPPR_ERR("%s:%d fail to set property:%s %d\n", property->name,
  217. (unsigned int)val, __func__, __LINE__);
  218. return -EINVAL;
  219. }
  220. static int mtk_plane_atomic_get_property(struct drm_plane *plane,
  221. const struct drm_plane_state *state,
  222. struct drm_property *property,
  223. uint64_t *val)
  224. {
  225. struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
  226. struct mtk_plane_state *plane_state = to_mtk_plane_state(state);
  227. int ret = 0;
  228. int i;
  229. if (!mtk_plane) {
  230. DDPPR_ERR("%s:%d mtk plane is null\n", __func__, __LINE__);
  231. return -EINVAL;
  232. }
  233. for (i = 0; i < PLANE_PROP_MAX; i++) {
  234. if (mtk_plane->plane_property[i] == property) {
  235. *val = plane_state->prop_val[i];
  236. DDPINFO("get property:%s %lld\n", property->name, *val);
  237. return ret;
  238. }
  239. }
  240. DDPPR_ERR("%s:%d fail to get property:%s %p\n", __func__, __LINE__,
  241. property->name, val);
  242. return -EINVAL;
  243. }
  244. static const struct drm_plane_funcs mtk_plane_funcs = {
  245. .update_plane = drm_atomic_helper_update_plane,
  246. .disable_plane = drm_atomic_helper_disable_plane,
  247. .destroy = drm_plane_cleanup,
  248. .reset = mtk_plane_reset,
  249. .atomic_duplicate_state = mtk_plane_duplicate_state,
  250. .atomic_destroy_state = mtk_drm_plane_destroy_state,
  251. .atomic_set_property = mtk_plane_atomic_set_property,
  252. .atomic_get_property = mtk_plane_atomic_get_property,
  253. };
  254. static int mtk_plane_atomic_check(struct drm_plane *plane,
  255. struct drm_plane_state *state)
  256. {
  257. struct drm_framebuffer *fb = state->fb;
  258. struct drm_crtc_state *crtc_state;
  259. struct drm_rect clip = {
  260. 0,
  261. };
  262. struct mtk_drm_private *private = plane->dev->dev_private;
  263. if (!fb)
  264. return 0;
  265. if (!mtk_fb_get_gem_obj(fb) && fb->format->format != DRM_FORMAT_C8) {
  266. DRM_DEBUG_KMS("buffer is null\n");
  267. return -EFAULT;
  268. }
  269. if (!state->crtc)
  270. return 0;
  271. crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
  272. if (IS_ERR(crtc_state))
  273. return PTR_ERR(crtc_state);
  274. clip.x2 = crtc_state->mode.hdisplay;
  275. clip.y2 = crtc_state->mode.vdisplay;
  276. if (mtk_drm_helper_get_opt(private->helper_opt, MTK_DRM_OPT_RPO))
  277. return drm_plane_helper_check_state(
  278. state, &clip, MTK_DRM_PLANE_SCALING_MIN,
  279. MTK_DRM_PLANE_SCALING_MAX, true, true);
  280. else
  281. return drm_plane_helper_check_state(
  282. state, &clip, DRM_PLANE_HELPER_NO_SCALING,
  283. DRM_PLANE_HELPER_NO_SCALING, true, true);
  284. }
  285. #ifdef MTK_DRM_ADVANCE
  286. static void _mtk_plane_get_comp_state(struct mtk_drm_lyeblob_ids *lyeblob_ids,
  287. struct mtk_plane_comp_state *comp_state,
  288. struct drm_crtc *crtc,
  289. unsigned int plane_index)
  290. {
  291. int blob_id;
  292. int ref_cnt;
  293. struct drm_property_blob *blob;
  294. blob_id = lyeblob_ids->lye_plane_blob_id[crtc->index][plane_index];
  295. ref_cnt = lyeblob_ids->ref_cnt;
  296. if (blob_id && ref_cnt) {
  297. blob = drm_property_lookup_blob(crtc->dev, blob_id);
  298. if (blob) {
  299. memcpy(comp_state, blob->data,
  300. sizeof(struct mtk_plane_comp_state));
  301. drm_property_unreference_blob(blob);
  302. }
  303. }
  304. }
  305. void mtk_plane_get_comp_state(struct drm_plane *plane,
  306. struct mtk_plane_comp_state *comp_state,
  307. struct drm_crtc *crtc, int lock)
  308. {
  309. struct mtk_drm_lyeblob_ids *lyeblob_ids, *next;
  310. struct mtk_drm_private *mtk_drm = crtc->dev->dev_private;
  311. struct mtk_crtc_state *crtc_state = to_mtk_crtc_state(crtc->state);
  312. unsigned int crtc_lye_idx = crtc_state->prop_val[CRTC_PROP_LYE_IDX];
  313. unsigned int plane_index = to_crtc_plane_index(plane->index);
  314. memset(comp_state, 0x0, sizeof(struct mtk_plane_comp_state));
  315. if (lock)
  316. mutex_lock(&mtk_drm->lyeblob_list_mutex);
  317. list_for_each_entry_safe(lyeblob_ids, next, &mtk_drm->lyeblob_head,
  318. list) {
  319. if (lyeblob_ids->lye_idx == crtc_lye_idx) {
  320. _mtk_plane_get_comp_state(lyeblob_ids, comp_state, crtc,
  321. plane_index);
  322. } else if (lyeblob_ids->lye_idx > crtc_lye_idx)
  323. break;
  324. }
  325. if (lock)
  326. mutex_unlock(&mtk_drm->lyeblob_list_mutex);
  327. }
  328. #endif
  329. static void mtk_plane_atomic_update(struct drm_plane *plane,
  330. struct drm_plane_state *old_state)
  331. {
  332. struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
  333. struct drm_crtc *crtc = plane->state->crtc;
  334. struct mtk_crtc_state *crtc_state;
  335. struct drm_framebuffer *fb = plane->state->fb;
  336. int src_w, src_h, dst_x, dst_y, dst_w, dst_h, i;
  337. struct mtk_drm_crtc *mtk_crtc;
  338. unsigned int plane_index = to_crtc_plane_index(plane->index);
  339. static int cnt;
  340. bool skip_update = 0;
  341. int crtc_index = 0;
  342. if (!crtc)
  343. return;
  344. crtc_state = to_mtk_crtc_state(crtc->state);
  345. mtk_crtc = to_mtk_crtc(crtc);
  346. crtc_index = drm_crtc_index(crtc);
  347. if ((!fb) || (mtk_crtc->ddp_mode == DDP_NO_USE))
  348. return;
  349. src_w = drm_rect_width(&plane->state->src) >> 16;
  350. src_h = drm_rect_height(&plane->state->src) >> 16;
  351. dst_x = plane->state->dst.x1;
  352. dst_y = plane->state->dst.y1;
  353. dst_w = drm_rect_width(&plane->state->dst);
  354. dst_h = drm_rect_height(&plane->state->dst);
  355. if (src_w < dst_w || src_h < dst_h) {
  356. dst_x = ((dst_x * src_w * 10) / dst_w + 5) / 10
  357. - crtc_state->rsz_src_roi.x;
  358. dst_y = ((dst_y * src_h * 10) / dst_h + 5) / 10
  359. - crtc_state->rsz_src_roi.y;
  360. dst_w = src_w;
  361. dst_h = src_h;
  362. }
  363. state->pending.enable = plane->state->visible;
  364. state->pending.pitch = fb->pitches[0];
  365. state->pending.format = fb->format->format;
  366. state->pending.modifier = fb->modifier[0];
  367. state->pending.addr = mtk_fb_get_dma(fb);
  368. state->pending.size = mtk_fb_get_size(fb);
  369. state->pending.src_x = (plane->state->src.x1 >> 16);
  370. state->pending.src_y = (plane->state->src.y1 >> 16);
  371. state->pending.dst_x = dst_x;
  372. state->pending.dst_y = dst_y;
  373. state->pending.width = dst_w;
  374. state->pending.height = dst_h;
  375. if (mtk_drm_fb_is_secure(fb))
  376. state->pending.is_sec = true;
  377. else
  378. state->pending.is_sec = false;
  379. for (i = 0; i < PLANE_PROP_MAX; i++)
  380. state->pending.prop_val[i] = state->prop_val[i];
  381. wmb(); /* Make sure the above parameters are set before update */
  382. state->pending.dirty = true;
  383. DDPINFO("%s:%d enable%d,pitch%d,format%d\n",
  384. __func__, __LINE__, (unsigned int)state->pending.enable,
  385. state->pending.pitch, state->pending.format);
  386. DDPINFO("addr0x%lx,x%d,y%d,width%d,height%d\n",
  387. (unsigned long)state->pending.addr, state->pending.dst_x,
  388. state->pending.dst_y, state->pending.width,
  389. state->pending.height);
  390. for (i = 0; i < PLANE_PROP_MAX; i++) {
  391. DDPINFO("prop_val[%d]:%d ", i,
  392. (unsigned int)state->pending.prop_val[i]);
  393. }
  394. DDPINFO("\n");
  395. DDPFENCE("S+/%sL%d/e%d/id%d/mva0x%08llx/size0x%08lx/S%d\n",
  396. mtk_crtc_index_spy(crtc_index),
  397. plane_index,
  398. state->pending.enable,
  399. state->pending.prop_val[PLANE_PROP_NEXT_BUFF_IDX],
  400. state->pending.addr,
  401. state->pending.size,
  402. state->pending.is_sec);
  403. if (!mtk_crtc->sec_on && state->pending.is_sec) {
  404. DDPMSG("receive sec buffer in non-sec mode\n");
  405. return;
  406. }
  407. if (state->pending.enable)
  408. atomic_set(&mtk_crtc->already_config, 1);
  409. if (cnt <= 5) {
  410. cnt++;
  411. if (state->pending.format == DRM_FORMAT_RGB565 &&
  412. drm_crtc_index(crtc) == 0)
  413. skip_update = 1;
  414. }
  415. /* workaround for skip plane update when hwc set crtc */
  416. if (skip_update == 0)
  417. mtk_drm_crtc_plane_update(crtc, plane, state);
  418. }
  419. static void mtk_plane_atomic_disable(struct drm_plane *plane,
  420. struct drm_plane_state *old_state)
  421. {
  422. struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
  423. state->pending.enable = false;
  424. wmb(); /* Make sure the above parameter is set before update */
  425. state->pending.dirty = true;
  426. #ifdef MTK_DRM_ADVANCE
  427. if (!state->crtc) {
  428. DDPPR_ERR("%s, empty crtc state\n", __func__);
  429. } else {
  430. mtk_drm_crtc_plane_update(state->crtc, plane, state);
  431. }
  432. #endif
  433. }
  434. static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
  435. .atomic_check = mtk_plane_atomic_check,
  436. .atomic_update = mtk_plane_atomic_update,
  437. .atomic_disable = mtk_plane_atomic_disable,
  438. };
  439. static void mtk_plane_attach_property(struct mtk_drm_plane *plane)
  440. {
  441. struct drm_device *dev = plane->base.dev;
  442. struct drm_property *prop;
  443. static struct drm_property *mtk_prop[PLANE_PROP_MAX];
  444. struct mtk_drm_property *plane_prop;
  445. int i;
  446. static int num;
  447. if (num == 0) {
  448. for (i = 0; i < PLANE_PROP_MAX; i++) {
  449. plane_prop = &(mtk_plane_property[i]);
  450. mtk_prop[i] = drm_property_create_range(
  451. dev, plane_prop->flags, plane_prop->name,
  452. plane_prop->min, plane_prop->max);
  453. if (!mtk_prop[i]) {
  454. DDPPR_ERR("fail to create property:%s\n",
  455. plane_prop->name);
  456. return;
  457. }
  458. DDPINFO("create property:%s, flags:0x%x\n",
  459. plane_prop->name, mtk_prop[i]->flags);
  460. }
  461. num++;
  462. }
  463. for (i = 0; i < PLANE_PROP_MAX; i++) {
  464. prop = plane->plane_property[i];
  465. plane_prop = &(mtk_plane_property[i]);
  466. if (!prop) {
  467. prop = mtk_prop[i];
  468. plane->plane_property[i] = prop;
  469. drm_object_attach_property(&plane->base.base, prop,
  470. plane_prop->val);
  471. }
  472. }
  473. }
  474. int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *plane,
  475. unsigned int zpos, unsigned long possible_crtcs,
  476. enum drm_plane_type type)
  477. {
  478. int err;
  479. err = drm_universal_plane_init(dev, &plane->base, possible_crtcs,
  480. &mtk_plane_funcs, formats,
  481. ARRAY_SIZE(formats), NULL,
  482. type, NULL);
  483. if (err) {
  484. DRM_ERROR("%s:%d failed to initialize plane\n", __func__,
  485. __LINE__);
  486. return err;
  487. }
  488. drm_plane_helper_add(&plane->base, &mtk_plane_helper_funcs);
  489. mtk_plane_attach_property(plane);
  490. return 0;
  491. }