ipuv3-plane.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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. cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
  57. if (!cma_obj) {
  58. DRM_DEBUG_KMS("entry is null.\n");
  59. return -EFAULT;
  60. }
  61. dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
  62. &cma_obj->paddr, x, y);
  63. ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
  64. eba = cma_obj->paddr + fb->offsets[0] +
  65. fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
  66. ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
  67. ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
  68. /* cache offsets for subsequent pageflips */
  69. ipu_plane->x = x;
  70. ipu_plane->y = y;
  71. return 0;
  72. }
  73. int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
  74. struct drm_display_mode *mode,
  75. struct drm_framebuffer *fb, int crtc_x, int crtc_y,
  76. unsigned int crtc_w, unsigned int crtc_h,
  77. uint32_t src_x, uint32_t src_y,
  78. uint32_t src_w, uint32_t src_h)
  79. {
  80. struct device *dev = ipu_plane->base.dev->dev;
  81. int ret;
  82. /* no scaling */
  83. if (src_w != crtc_w || src_h != crtc_h)
  84. return -EINVAL;
  85. /* clip to crtc bounds */
  86. if (crtc_x < 0) {
  87. if (-crtc_x > crtc_w)
  88. return -EINVAL;
  89. src_x += -crtc_x;
  90. src_w -= -crtc_x;
  91. crtc_w -= -crtc_x;
  92. crtc_x = 0;
  93. }
  94. if (crtc_y < 0) {
  95. if (-crtc_y > crtc_h)
  96. return -EINVAL;
  97. src_y += -crtc_y;
  98. src_h -= -crtc_y;
  99. crtc_h -= -crtc_y;
  100. crtc_y = 0;
  101. }
  102. if (crtc_x + crtc_w > mode->hdisplay) {
  103. if (crtc_x > mode->hdisplay)
  104. return -EINVAL;
  105. crtc_w = mode->hdisplay - crtc_x;
  106. src_w = crtc_w;
  107. }
  108. if (crtc_y + crtc_h > mode->vdisplay) {
  109. if (crtc_y > mode->vdisplay)
  110. return -EINVAL;
  111. crtc_h = mode->vdisplay - crtc_y;
  112. src_h = crtc_h;
  113. }
  114. /* full plane minimum width is 13 pixels */
  115. if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
  116. return -EINVAL;
  117. if (crtc_h < 2)
  118. return -EINVAL;
  119. switch (ipu_plane->dp_flow) {
  120. case IPU_DP_FLOW_SYNC_BG:
  121. ret = ipu_dp_setup_channel(ipu_plane->dp,
  122. IPUV3_COLORSPACE_RGB,
  123. IPUV3_COLORSPACE_RGB);
  124. if (ret) {
  125. dev_err(dev,
  126. "initializing display processor failed with %d\n",
  127. ret);
  128. return ret;
  129. }
  130. ipu_dp_set_global_alpha(ipu_plane->dp, 1, 0, 1);
  131. break;
  132. case IPU_DP_FLOW_SYNC_FG:
  133. ipu_dp_setup_channel(ipu_plane->dp,
  134. ipu_drm_fourcc_to_colorspace(fb->pixel_format),
  135. IPUV3_COLORSPACE_UNKNOWN);
  136. ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
  137. break;
  138. }
  139. ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
  140. if (ret) {
  141. dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
  142. return ret;
  143. }
  144. ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
  145. calc_bandwidth(crtc_w, crtc_h,
  146. calc_vref(mode)), 64);
  147. if (ret) {
  148. dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
  149. return ret;
  150. }
  151. ipu_cpmem_zero(ipu_plane->ipu_ch);
  152. ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
  153. ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
  154. if (ret < 0) {
  155. dev_err(dev, "unsupported pixel format 0x%08x\n",
  156. fb->pixel_format);
  157. return ret;
  158. }
  159. ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
  160. ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
  161. if (ret < 0)
  162. return ret;
  163. return 0;
  164. }
  165. void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
  166. {
  167. if (!IS_ERR_OR_NULL(ipu_plane->dp))
  168. ipu_dp_put(ipu_plane->dp);
  169. if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
  170. ipu_dmfc_put(ipu_plane->dmfc);
  171. if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
  172. ipu_idmac_put(ipu_plane->ipu_ch);
  173. }
  174. int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
  175. {
  176. int ret;
  177. ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
  178. if (IS_ERR(ipu_plane->ipu_ch)) {
  179. ret = PTR_ERR(ipu_plane->ipu_ch);
  180. DRM_ERROR("failed to get idmac channel: %d\n", ret);
  181. return ret;
  182. }
  183. ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
  184. if (IS_ERR(ipu_plane->dmfc)) {
  185. ret = PTR_ERR(ipu_plane->dmfc);
  186. DRM_ERROR("failed to get dmfc: ret %d\n", ret);
  187. goto err_out;
  188. }
  189. if (ipu_plane->dp_flow >= 0) {
  190. ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
  191. if (IS_ERR(ipu_plane->dp)) {
  192. ret = PTR_ERR(ipu_plane->dp);
  193. DRM_ERROR("failed to get dp flow: %d\n", ret);
  194. goto err_out;
  195. }
  196. }
  197. return 0;
  198. err_out:
  199. ipu_plane_put_resources(ipu_plane);
  200. return ret;
  201. }
  202. void ipu_plane_enable(struct ipu_plane *ipu_plane)
  203. {
  204. if (ipu_plane->dp)
  205. ipu_dp_enable(ipu_plane->ipu);
  206. ipu_dmfc_enable_channel(ipu_plane->dmfc);
  207. ipu_idmac_enable_channel(ipu_plane->ipu_ch);
  208. if (ipu_plane->dp)
  209. ipu_dp_enable_channel(ipu_plane->dp);
  210. ipu_plane->enabled = true;
  211. }
  212. void ipu_plane_disable(struct ipu_plane *ipu_plane)
  213. {
  214. ipu_plane->enabled = false;
  215. ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
  216. if (ipu_plane->dp)
  217. ipu_dp_disable_channel(ipu_plane->dp);
  218. ipu_idmac_disable_channel(ipu_plane->ipu_ch);
  219. ipu_dmfc_disable_channel(ipu_plane->dmfc);
  220. if (ipu_plane->dp)
  221. ipu_dp_disable(ipu_plane->ipu);
  222. }
  223. /*
  224. * drm_plane API
  225. */
  226. static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
  227. struct drm_framebuffer *fb, int crtc_x, int crtc_y,
  228. unsigned int crtc_w, unsigned int crtc_h,
  229. uint32_t src_x, uint32_t src_y,
  230. uint32_t src_w, uint32_t src_h)
  231. {
  232. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  233. int ret = 0;
  234. DRM_DEBUG_KMS("plane - %p\n", plane);
  235. if (!ipu_plane->enabled)
  236. ret = ipu_plane_get_resources(ipu_plane);
  237. if (ret < 0)
  238. return ret;
  239. ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
  240. crtc_x, crtc_y, crtc_w, crtc_h,
  241. src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16);
  242. if (ret < 0) {
  243. ipu_plane_put_resources(ipu_plane);
  244. return ret;
  245. }
  246. if (crtc != plane->crtc)
  247. dev_info(plane->dev->dev, "crtc change: %p -> %p\n",
  248. plane->crtc, crtc);
  249. plane->crtc = crtc;
  250. if (!ipu_plane->enabled)
  251. ipu_plane_enable(ipu_plane);
  252. return 0;
  253. }
  254. static int ipu_disable_plane(struct drm_plane *plane)
  255. {
  256. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  257. DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
  258. if (ipu_plane->enabled)
  259. ipu_plane_disable(ipu_plane);
  260. ipu_plane_put_resources(ipu_plane);
  261. return 0;
  262. }
  263. static void ipu_plane_destroy(struct drm_plane *plane)
  264. {
  265. struct ipu_plane *ipu_plane = to_ipu_plane(plane);
  266. DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
  267. ipu_disable_plane(plane);
  268. drm_plane_cleanup(plane);
  269. kfree(ipu_plane);
  270. }
  271. static struct drm_plane_funcs ipu_plane_funcs = {
  272. .update_plane = ipu_update_plane,
  273. .disable_plane = ipu_disable_plane,
  274. .destroy = ipu_plane_destroy,
  275. };
  276. struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
  277. int dma, int dp, unsigned int possible_crtcs,
  278. bool priv)
  279. {
  280. struct ipu_plane *ipu_plane;
  281. int ret;
  282. DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
  283. dma, dp, possible_crtcs);
  284. ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
  285. if (!ipu_plane) {
  286. DRM_ERROR("failed to allocate plane\n");
  287. return ERR_PTR(-ENOMEM);
  288. }
  289. ipu_plane->ipu = ipu;
  290. ipu_plane->dma = dma;
  291. ipu_plane->dp_flow = dp;
  292. ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs,
  293. &ipu_plane_funcs, ipu_plane_formats,
  294. ARRAY_SIZE(ipu_plane_formats),
  295. priv);
  296. if (ret) {
  297. DRM_ERROR("failed to initialize plane\n");
  298. kfree(ipu_plane);
  299. return ERR_PTR(ret);
  300. }
  301. return ipu_plane;
  302. }