ipuv3-plane.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * i.MX IPUv3 DP Overlay Planes
  3. *
  4. * Copyright (C) 2013 Philipp Zabel, Pengutronix
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <drm/drmP.h>
  16. #include <drm/drm_fb_cma_helper.h>
  17. #include <drm/drm_gem_cma_helper.h>
  18. #include "video/imx-ipu-v3.h"
  19. #include "ipuv3-plane.h"
  20. #define to_ipu_plane(x) container_of(x, struct ipu_plane, base)
  21. static const uint32_t ipu_plane_formats[] = {
  22. DRM_FORMAT_XRGB1555,
  23. DRM_FORMAT_XBGR1555,
  24. DRM_FORMAT_ARGB8888,
  25. DRM_FORMAT_XRGB8888,
  26. DRM_FORMAT_ABGR8888,
  27. DRM_FORMAT_XBGR8888,
  28. DRM_FORMAT_YUYV,
  29. DRM_FORMAT_YVYU,
  30. DRM_FORMAT_YUV420,
  31. DRM_FORMAT_YVU420,
  32. };
  33. int ipu_plane_irq(struct ipu_plane *ipu_plane)
  34. {
  35. return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
  36. IPU_IRQ_EOF);
  37. }
  38. static int calc_vref(struct drm_display_mode *mode)
  39. {
  40. unsigned long htotal, vtotal;
  41. htotal = mode->htotal;
  42. vtotal = mode->vtotal;
  43. if (!htotal || !vtotal)
  44. return 60;
  45. return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
  46. }
  47. static inline int calc_bandwidth(int width, int height, unsigned int vref)
  48. {
  49. return width * height * vref;
  50. }
  51. int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
  52. int x, int y)
  53. {
  54. struct drm_gem_cma_object *cma_obj;
  55. unsigned long eba;
  56. int active;
  57. cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
  58. if (!cma_obj) {
  59. DRM_DEBUG_KMS("entry is null.\n");
  60. return -EFAULT;
  61. }
  62. dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
  63. &cma_obj->paddr, x, y);
  64. eba = cma_obj->paddr + fb->offsets[0] +
  65. fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
  66. if (ipu_plane->enabled) {
  67. active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
  68. ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
  69. ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
  70. } else {
  71. ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
  72. ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
  73. }
  74. /* cache offsets for subsequent pageflips */
  75. ipu_plane->x = x;
  76. ipu_plane->y = y;
  77. return 0;
  78. }
  79. int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
  80. struct drm_display_mode *mode,
  81. struct drm_framebuffer *fb, int crtc_x, int crtc_y,
  82. unsigned int crtc_w, unsigned int crtc_h,
  83. uint32_t src_x, uint32_t src_y,
  84. uint32_t src_w, uint32_t src_h, bool interlaced)
  85. {
  86. struct device *dev = ipu_plane->base.dev->dev;
  87. int ret;
  88. /* no scaling */
  89. if (src_w != crtc_w || src_h != crtc_h)
  90. return -EINVAL;
  91. /* clip to crtc bounds */
  92. if (crtc_x < 0) {
  93. if (-crtc_x > crtc_w)
  94. return -EINVAL;
  95. src_x += -crtc_x;
  96. src_w -= -crtc_x;
  97. crtc_w -= -crtc_x;
  98. crtc_x = 0;
  99. }
  100. if (crtc_y < 0) {
  101. if (-crtc_y > crtc_h)
  102. return -EINVAL;
  103. src_y += -crtc_y;
  104. src_h -= -crtc_y;
  105. crtc_h -= -crtc_y;
  106. crtc_y = 0;
  107. }
  108. if (crtc_x + crtc_w > mode->hdisplay) {
  109. if (crtc_x > mode->hdisplay)
  110. return -EINVAL;
  111. crtc_w = mode->hdisplay - crtc_x;
  112. src_w = crtc_w;
  113. }
  114. if (crtc_y + crtc_h > mode->vdisplay) {
  115. if (crtc_y > mode->vdisplay)
  116. return -EINVAL;
  117. crtc_h = mode->vdisplay - crtc_y;
  118. src_h = crtc_h;
  119. }
  120. /* full plane minimum width is 13 pixels */
  121. if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
  122. return -EINVAL;
  123. if (crtc_h < 2)
  124. return -EINVAL;
  125. /*
  126. * since we cannot touch active IDMAC channels, we do not support
  127. * resizing the enabled plane or changing its format
  128. */
  129. if (ipu_plane->enabled) {
  130. if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
  131. fb->pixel_format != ipu_plane->base.fb->pixel_format)
  132. return -EINVAL;
  133. return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
  134. }
  135. switch (ipu_plane->dp_flow) {
  136. case IPU_DP_FLOW_SYNC_BG:
  137. ret = ipu_dp_setup_channel(ipu_plane->dp,
  138. IPUV3_COLORSPACE_RGB,
  139. IPUV3_COLORSPACE_RGB);
  140. if (ret) {
  141. dev_err(dev,
  142. "initializing display processor failed with %d\n",
  143. ret);
  144. return ret;
  145. }
  146. ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
  147. break;
  148. case IPU_DP_FLOW_SYNC_FG:
  149. ipu_dp_setup_channel(ipu_plane->dp,
  150. ipu_drm_fourcc_to_colorspace(fb->pixel_format),
  151. IPUV3_COLORSPACE_UNKNOWN);
  152. ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
  153. /* Enable local alpha on partial plane */
  154. switch (fb->pixel_format) {
  155. case DRM_FORMAT_ARGB8888:
  156. case DRM_FORMAT_ABGR8888:
  157. ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
  158. break;
  159. default:
  160. break;
  161. }
  162. }
  163. ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
  164. if (ret) {
  165. dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
  166. return ret;
  167. }
  168. ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
  169. calc_bandwidth(crtc_w, crtc_h,
  170. calc_vref(mode)), 64);
  171. if (ret) {
  172. dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
  173. return ret;
  174. }
  175. ipu_cpmem_zero(ipu_plane->ipu_ch);
  176. ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
  177. ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
  178. if (ret < 0) {
  179. dev_err(dev, "unsupported pixel format 0x%08x\n",
  180. fb->pixel_format);
  181. return ret;
  182. }
  183. ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
  184. ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
  185. ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
  186. ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
  187. if (ret < 0)
  188. return ret;
  189. if (interlaced)
  190. ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]);
  191. ipu_plane->w = src_w;
  192. ipu_plane->h = src_h;
  193. return 0;
  194. }
  195. void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
  196. {
  197. if (!IS_ERR_OR_NULL(ipu_plane->dp))
  198. ipu_dp_put(ipu_plane->dp);
  199. if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
  200. ipu_dmfc_put(ipu_plane->dmfc);
  201. if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
  202. ipu_idmac_put(ipu_plane->ipu_ch);
  203. }
  204. int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
  205. {
  206. int ret;
  207. ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
  208. if (IS_ERR(ipu_plane->ipu_ch)) {
  209. ret = PTR_ERR(ipu_plane->ipu_ch);
  210. DRM_ERROR("failed to get idmac channel: %d\n", ret);
  211. return ret;
  212. }
  213. ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
  214. if (IS_ERR(ipu_plane->dmfc)) {
  215. ret = PTR_ERR(ipu_plane->dmfc);
  216. DRM_ERROR("failed to get dmfc: ret %d\n", ret);
  217. goto err_out;
  218. }
  219. if (ipu_plane->dp_flow >= 0) {
  220. ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
  221. if (IS_ERR(ipu_plane->dp)) {
  222. ret = PTR_ERR(ipu_plane->dp);
  223. DRM_ERROR("failed to get dp flow: %d\n", ret);
  224. goto err_out;
  225. }
  226. }
  227. return 0;
  228. err_out:
  229. ipu_plane_put_resources(ipu_plane);
  230. return ret;
  231. }
  232. void ipu_plane_enable(struct ipu_plane *ipu_plane)
  233. {
  234. if (ipu_plane->dp)
  235. ipu_dp_enable(ipu_plane->ipu);
  236. ipu_dmfc_enable_channel(ipu_plane->dmfc);
  237. ipu_idmac_enable_channel(ipu_plane->ipu_ch);
  238. if (ipu_plane->dp)
  239. ipu_dp_enable_channel(ipu_plane->dp);
  240. ipu_plane->enabled = true;
  241. }
  242. void ipu_plane_disable(struct ipu_plane *ipu_plane)
  243. {
  244. ipu_plane->enabled = false;
  245. ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
  246. if (ipu_plane->dp)
  247. ipu_dp_disable_channel(ipu_plane->dp);
  248. ipu_idmac_disable_channel(ipu_plane->ipu_ch);
  249. ipu_dmfc_disable_channel(ipu_plane->dmfc);
  250. if (ipu_plane->dp)
  251. ipu_dp_disable(ipu_plane->ipu);
  252. }
  253. /*
  254. * drm_plane API
  255. */
  256. static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
  257. struct drm_framebuffer *fb, int crtc_x, int crtc_y,
  258. unsigned int crtc_w, unsigned int crtc_h,
  259. uint32_t src_x, uint32_t src_y,
  260. uint32_t src_w, uint32_t src_h)
  261. {
  262. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  263. int ret = 0;
  264. DRM_DEBUG_KMS("plane - %p\n", plane);
  265. if (!ipu_plane->enabled)
  266. ret = ipu_plane_get_resources(ipu_plane);
  267. if (ret < 0)
  268. return ret;
  269. ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
  270. crtc_x, crtc_y, crtc_w, crtc_h,
  271. src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
  272. false);
  273. if (ret < 0) {
  274. ipu_plane_put_resources(ipu_plane);
  275. return ret;
  276. }
  277. if (crtc != plane->crtc)
  278. dev_info(plane->dev->dev, "crtc change: %p -> %p\n",
  279. plane->crtc, crtc);
  280. plane->crtc = crtc;
  281. if (!ipu_plane->enabled)
  282. ipu_plane_enable(ipu_plane);
  283. return 0;
  284. }
  285. static int ipu_disable_plane(struct drm_plane *plane)
  286. {
  287. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  288. DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
  289. if (ipu_plane->enabled)
  290. ipu_plane_disable(ipu_plane);
  291. ipu_plane_put_resources(ipu_plane);
  292. return 0;
  293. }
  294. static void ipu_plane_destroy(struct drm_plane *plane)
  295. {
  296. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  297. DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
  298. ipu_disable_plane(plane);
  299. drm_plane_cleanup(plane);
  300. kfree(ipu_plane);
  301. }
  302. static struct drm_plane_funcs ipu_plane_funcs = {
  303. .update_plane = ipu_update_plane,
  304. .disable_plane = ipu_disable_plane,
  305. .destroy = ipu_plane_destroy,
  306. };
  307. struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
  308. int dma, int dp, unsigned int possible_crtcs,
  309. bool priv)
  310. {
  311. struct ipu_plane *ipu_plane;
  312. int ret;
  313. DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
  314. dma, dp, possible_crtcs);
  315. ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
  316. if (!ipu_plane) {
  317. DRM_ERROR("failed to allocate plane\n");
  318. return ERR_PTR(-ENOMEM);
  319. }
  320. ipu_plane->ipu = ipu;
  321. ipu_plane->dma = dma;
  322. ipu_plane->dp_flow = dp;
  323. ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs,
  324. &ipu_plane_funcs, ipu_plane_formats,
  325. ARRAY_SIZE(ipu_plane_formats),
  326. priv);
  327. if (ret) {
  328. DRM_ERROR("failed to initialize plane\n");
  329. kfree(ipu_plane);
  330. return ERR_PTR(ret);
  331. }
  332. return ipu_plane;
  333. }