mdp5_smp.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * Copyright (c) 2014, The Linux Foundation. All rights reserved.
  3. * Copyright (C) 2013 Red Hat
  4. * Author: Rob Clark <robdclark@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License version 2 as published by
  8. * the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with
  16. * this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "mdp5_kms.h"
  19. #include "mdp5_smp.h"
  20. /* SMP - Shared Memory Pool
  21. *
  22. * These are shared between all the clients, where each plane in a
  23. * scanout buffer is a SMP client. Ie. scanout of 3 plane I420 on
  24. * pipe VIG0 => 3 clients: VIG0_Y, VIG0_CB, VIG0_CR.
  25. *
  26. * Based on the size of the attached scanout buffer, a certain # of
  27. * blocks must be allocated to that client out of the shared pool.
  28. *
  29. * In some hw, some blocks are statically allocated for certain pipes
  30. * and CANNOT be re-allocated (eg: MMB0 and MMB1 both tied to RGB0).
  31. *
  32. * For each block that can be dynamically allocated, it can be either
  33. * free, or pending/in-use by a client. The updates happen in three steps:
  34. *
  35. * 1) mdp5_smp_request():
  36. * When plane scanout is setup, calculate required number of
  37. * blocks needed per client, and request. Blocks not inuse or
  38. * pending by any other client are added to client's pending
  39. * set.
  40. *
  41. * 2) mdp5_smp_configure():
  42. * As hw is programmed, before FLUSH, MDP5_MDP_SMP_ALLOC registers
  43. * are configured for the union(pending, inuse)
  44. *
  45. * 3) mdp5_smp_commit():
  46. * After next vblank, copy pending -> inuse. Optionally update
  47. * MDP5_SMP_ALLOC registers if there are newly unused blocks
  48. *
  49. * On the next vblank after changes have been committed to hw, the
  50. * client's pending blocks become it's in-use blocks (and no-longer
  51. * in-use blocks become available to other clients).
  52. *
  53. * btw, hurray for confusing overloaded acronyms! :-/
  54. *
  55. * NOTE: for atomic modeset/pageflip NONBLOCK operations, step #1
  56. * should happen at (or before)? atomic->check(). And we'd need
  57. * an API to discard previous requests if update is aborted or
  58. * (test-only).
  59. *
  60. * TODO would perhaps be nice to have debugfs to dump out kernel
  61. * inuse and pending state of all clients..
  62. */
  63. struct mdp5_smp {
  64. struct drm_device *dev;
  65. int blk_cnt;
  66. int blk_size;
  67. spinlock_t state_lock;
  68. mdp5_smp_state_t state; /* to track smp allocation amongst pipes: */
  69. struct mdp5_client_smp_state client_state[MAX_CLIENTS];
  70. };
  71. static inline
  72. struct mdp5_kms *get_kms(struct mdp5_smp *smp)
  73. {
  74. struct msm_drm_private *priv = smp->dev->dev_private;
  75. return to_mdp5_kms(to_mdp_kms(priv->kms));
  76. }
  77. static inline u32 pipe2client(enum mdp5_pipe pipe, int plane)
  78. {
  79. #define CID_UNUSED 0
  80. if (WARN_ON(plane >= pipe2nclients(pipe)))
  81. return CID_UNUSED;
  82. /*
  83. * Note on SMP clients:
  84. * For ViG pipes, fetch Y/Cr/Cb-components clients are always
  85. * consecutive, and in that order.
  86. *
  87. * e.g.:
  88. * if mdp5_cfg->smp.clients[SSPP_VIG0] = N,
  89. * Y plane's client ID is N
  90. * Cr plane's client ID is N + 1
  91. * Cb plane's client ID is N + 2
  92. */
  93. return mdp5_cfg->smp.clients[pipe] + plane;
  94. }
  95. /* step #1: update # of blocks pending for the client: */
  96. static int smp_request_block(struct mdp5_smp *smp,
  97. u32 cid, int nblks)
  98. {
  99. struct mdp5_kms *mdp5_kms = get_kms(smp);
  100. const struct mdp5_cfg_hw *hw_cfg;
  101. struct mdp5_client_smp_state *ps = &smp->client_state[cid];
  102. int i, ret, avail, cur_nblks, cnt = smp->blk_cnt;
  103. int reserved;
  104. unsigned long flags;
  105. hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
  106. reserved = hw_cfg->smp.reserved[cid];
  107. spin_lock_irqsave(&smp->state_lock, flags);
  108. if (reserved) {
  109. nblks = max(0, nblks - reserved);
  110. DBG("%d MMBs allocated (%d reserved)", nblks, reserved);
  111. }
  112. avail = cnt - bitmap_weight(smp->state, cnt);
  113. if (nblks > avail) {
  114. dev_err(mdp5_kms->dev->dev, "out of blks (req=%d > avail=%d)\n",
  115. nblks, avail);
  116. ret = -ENOSPC;
  117. goto fail;
  118. }
  119. cur_nblks = bitmap_weight(ps->pending, cnt);
  120. if (nblks > cur_nblks) {
  121. /* grow the existing pending reservation: */
  122. for (i = cur_nblks; i < nblks; i++) {
  123. int blk = find_first_zero_bit(smp->state, cnt);
  124. set_bit(blk, ps->pending);
  125. set_bit(blk, smp->state);
  126. }
  127. } else {
  128. /* shrink the existing pending reservation: */
  129. for (i = cur_nblks; i > nblks; i--) {
  130. int blk = find_first_bit(ps->pending, cnt);
  131. clear_bit(blk, ps->pending);
  132. /* don't clear in global smp_state until _commit() */
  133. }
  134. }
  135. fail:
  136. spin_unlock_irqrestore(&smp->state_lock, flags);
  137. return 0;
  138. }
  139. static void set_fifo_thresholds(struct mdp5_smp *smp,
  140. enum mdp5_pipe pipe, int nblks)
  141. {
  142. struct mdp5_kms *mdp5_kms = get_kms(smp);
  143. u32 smp_entries_per_blk = smp->blk_size / (128 / BITS_PER_BYTE);
  144. u32 val;
  145. /* 1/4 of SMP pool that is being fetched */
  146. val = (nblks * smp_entries_per_blk) / 4;
  147. mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1);
  148. mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2);
  149. mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3);
  150. }
  151. /*
  152. * NOTE: looks like if horizontal decimation is used (if we supported that)
  153. * then the width used to calculate SMP block requirements is the post-
  154. * decimated width. Ie. SMP buffering sits downstream of decimation (which
  155. * presumably happens during the dma from scanout buffer).
  156. */
  157. int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, u32 fmt, u32 width)
  158. {
  159. struct mdp5_kms *mdp5_kms = get_kms(smp);
  160. struct drm_device *dev = mdp5_kms->dev;
  161. int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg);
  162. int i, hsub, nplanes, nlines, nblks, ret;
  163. nplanes = drm_format_num_planes(fmt);
  164. hsub = drm_format_horz_chroma_subsampling(fmt);
  165. /* different if BWC (compressed framebuffer?) enabled: */
  166. nlines = 2;
  167. for (i = 0, nblks = 0; i < nplanes; i++) {
  168. int n, fetch_stride, cpp;
  169. cpp = drm_format_plane_cpp(fmt, i);
  170. fetch_stride = width * cpp / (i ? hsub : 1);
  171. n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size);
  172. /* for hw rev v1.00 */
  173. if (rev == 0)
  174. n = roundup_pow_of_two(n);
  175. DBG("%s[%d]: request %d SMP blocks", pipe2name(pipe), i, n);
  176. ret = smp_request_block(smp, pipe2client(pipe, i), n);
  177. if (ret) {
  178. dev_err(dev->dev, "Cannot allocate %d SMP blocks: %d\n",
  179. n, ret);
  180. return ret;
  181. }
  182. nblks += n;
  183. }
  184. set_fifo_thresholds(smp, pipe, nblks);
  185. return 0;
  186. }
  187. /* Release SMP blocks for all clients of the pipe */
  188. void mdp5_smp_release(struct mdp5_smp *smp, enum mdp5_pipe pipe)
  189. {
  190. int i, nblks;
  191. for (i = 0, nblks = 0; i < pipe2nclients(pipe); i++)
  192. smp_request_block(smp, pipe2client(pipe, i), 0);
  193. set_fifo_thresholds(smp, pipe, 0);
  194. }
  195. static void update_smp_state(struct mdp5_smp *smp,
  196. u32 cid, mdp5_smp_state_t *assigned)
  197. {
  198. struct mdp5_kms *mdp5_kms = get_kms(smp);
  199. int cnt = smp->blk_cnt;
  200. u32 blk, val;
  201. for_each_set_bit(blk, *assigned, cnt) {
  202. int idx = blk / 3;
  203. int fld = blk % 3;
  204. val = mdp5_read(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx));
  205. switch (fld) {
  206. case 0:
  207. val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__MASK;
  208. val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0(cid);
  209. break;
  210. case 1:
  211. val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__MASK;
  212. val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1(cid);
  213. break;
  214. case 2:
  215. val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__MASK;
  216. val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2(cid);
  217. break;
  218. }
  219. mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx), val);
  220. mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_R_REG(0, idx), val);
  221. }
  222. }
  223. /* step #2: configure hw for union(pending, inuse): */
  224. void mdp5_smp_configure(struct mdp5_smp *smp, enum mdp5_pipe pipe)
  225. {
  226. int cnt = smp->blk_cnt;
  227. mdp5_smp_state_t assigned;
  228. int i;
  229. for (i = 0; i < pipe2nclients(pipe); i++) {
  230. u32 cid = pipe2client(pipe, i);
  231. struct mdp5_client_smp_state *ps = &smp->client_state[cid];
  232. bitmap_or(assigned, ps->inuse, ps->pending, cnt);
  233. update_smp_state(smp, cid, &assigned);
  234. }
  235. }
  236. /* step #3: after vblank, copy pending -> inuse: */
  237. void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe)
  238. {
  239. int cnt = smp->blk_cnt;
  240. mdp5_smp_state_t released;
  241. int i;
  242. for (i = 0; i < pipe2nclients(pipe); i++) {
  243. u32 cid = pipe2client(pipe, i);
  244. struct mdp5_client_smp_state *ps = &smp->client_state[cid];
  245. /*
  246. * Figure out if there are any blocks we where previously
  247. * using, which can be released and made available to other
  248. * clients:
  249. */
  250. if (bitmap_andnot(released, ps->inuse, ps->pending, cnt)) {
  251. unsigned long flags;
  252. spin_lock_irqsave(&smp->state_lock, flags);
  253. /* clear released blocks: */
  254. bitmap_andnot(smp->state, smp->state, released, cnt);
  255. spin_unlock_irqrestore(&smp->state_lock, flags);
  256. update_smp_state(smp, CID_UNUSED, &released);
  257. }
  258. bitmap_copy(ps->inuse, ps->pending, cnt);
  259. }
  260. }
  261. void mdp5_smp_destroy(struct mdp5_smp *smp)
  262. {
  263. kfree(smp);
  264. }
  265. struct mdp5_smp *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_block *cfg)
  266. {
  267. struct mdp5_smp *smp = NULL;
  268. int ret;
  269. smp = kzalloc(sizeof(*smp), GFP_KERNEL);
  270. if (unlikely(!smp)) {
  271. ret = -ENOMEM;
  272. goto fail;
  273. }
  274. smp->dev = dev;
  275. smp->blk_cnt = cfg->mmb_count;
  276. smp->blk_size = cfg->mmb_size;
  277. /* statically tied MMBs cannot be re-allocated: */
  278. bitmap_copy(smp->state, cfg->reserved_state, smp->blk_cnt);
  279. spin_lock_init(&smp->state_lock);
  280. return smp;
  281. fail:
  282. if (smp)
  283. mdp5_smp_destroy(smp);
  284. return ERR_PTR(ret);
  285. }