cregit-Linux how code gets into the kernel

Release 4.7 drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c

/*
 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "mdp5_kms.h"
#include "mdp5_smp.h"


/* SMP - Shared Memory Pool
 *
 * These are shared between all the clients, where each plane in a
 * scanout buffer is a SMP client.  Ie. scanout of 3 plane I420 on
 * pipe VIG0 => 3 clients: VIG0_Y, VIG0_CB, VIG0_CR.
 *
 * Based on the size of the attached scanout buffer, a certain # of
 * blocks must be allocated to that client out of the shared pool.
 *
 * In some hw, some blocks are statically allocated for certain pipes
 * and CANNOT be re-allocated (eg: MMB0 and MMB1 both tied to RGB0).
 *
 * For each block that can be dynamically allocated, it can be either
 *     free:
 *     The block is free.
 *
 *     pending:
 *     The block is allocated to some client and not free.
 *
 *     configured:
 *     The block is allocated to some client, and assigned to that
 *     client in MDP5_MDP_SMP_ALLOC registers.
 *
 *     inuse:
 *     The block is being actively used by a client.
 *
 * The updates happen in the following steps:
 *
 *  1) mdp5_smp_request():
 *     When plane scanout is setup, calculate required number of
 *     blocks needed per client, and request. Blocks neither inuse nor
 *     configured nor pending by any other client are added to client's
 *     pending set.
 *     For shrinking, blocks in pending but not in configured can be freed
 *     directly, but those already in configured will be freed later by
 *     mdp5_smp_commit.
 *
 *  2) mdp5_smp_configure():
 *     As hw is programmed, before FLUSH, MDP5_MDP_SMP_ALLOC registers
 *     are configured for the union(pending, inuse)
 *     Current pending is copied to configured.
 *     It is assumed that mdp5_smp_request and mdp5_smp_configure not run
 *     concurrently for the same pipe.
 *
 *  3) mdp5_smp_commit():
 *     After next vblank, copy configured -> inuse.  Optionally update
 *     MDP5_SMP_ALLOC registers if there are newly unused blocks
 *
 *  4) mdp5_smp_release():
 *     Must be called after the pipe is disabled and no longer uses any SMB
 *
 * On the next vblank after changes have been committed to hw, the
 * client's pending blocks become it's in-use blocks (and no-longer
 * in-use blocks become available to other clients).
 *
 * btw, hurray for confusing overloaded acronyms!  :-/
 *
 * NOTE: for atomic modeset/pageflip NONBLOCK operations, step #1
 * should happen at (or before)? atomic->check().  And we'd need
 * an API to discard previous requests if update is aborted or
 * (test-only).
 *
 * TODO would perhaps be nice to have debugfs to dump out kernel
 * inuse and pending state of all clients..
 */


struct mdp5_smp {
	
struct drm_device *dev;

	
uint8_t reserved[MAX_CLIENTS]; /* fixed MMBs allocation per client */

	
int blk_cnt;
	
int blk_size;

	
spinlock_t state_lock;
	
mdp5_smp_state_t state; /* to track smp allocation amongst pipes: */

	
struct mdp5_client_smp_state client_state[MAX_CLIENTS];
};

static void update_smp_state(struct mdp5_smp *smp,
		u32 cid, mdp5_smp_state_t *assigned);


static inline struct mdp5_kms *get_kms(struct mdp5_smp *smp) { struct msm_drm_private *priv = smp->dev->dev_private; return to_mdp5_kms(to_mdp_kms(priv->kms)); }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau36100.00%1100.00%
Total36100.00%1100.00%


static inline u32 pipe2client(enum mdp5_pipe pipe, int plane) { #define CID_UNUSED 0 if (WARN_ON(plane >= pipe2nclients(pipe))) return CID_UNUSED; /* * Note on SMP clients: * For ViG pipes, fetch Y/Cr/Cb-components clients are always * consecutive, and in that order. * * e.g.: * if mdp5_cfg->smp.clients[SSPP_VIG0] = N, * Y plane's client ID is N * Cr plane's client ID is N + 1 * Cb plane's client ID is N + 2 */ return mdp5_cfg->smp.clients[pipe] + plane; }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau4393.48%266.67%
rob clarkrob clark36.52%133.33%
Total46100.00%3100.00%

/* step #1: update # of blocks pending for the client: */
static int smp_request_block(struct mdp5_smp *smp, u32 cid, int nblks) { struct mdp5_kms *mdp5_kms = get_kms(smp); struct mdp5_client_smp_state *ps = &smp->client_state[cid]; int i, ret, avail, cur_nblks, cnt = smp->blk_cnt; uint8_t reserved; unsigned long flags; reserved = smp->reserved[cid]; spin_lock_irqsave(&smp->state_lock, flags); if (reserved) { nblks = max(0, nblks - reserved); DBG("%d MMBs allocated (%d reserved)", nblks, reserved); } avail = cnt - bitmap_weight(smp->state, cnt); if (nblks > avail) { dev_err(mdp5_kms->dev->dev, "out of blks (req=%d > avail=%d)\n", nblks, avail); ret = -ENOSPC; goto fail; } cur_nblks = bitmap_weight(ps->pending, cnt); if (nblks > cur_nblks) { /* grow the existing pending reservation: */ for (i = cur_nblks; i < nblks; i++) { int blk = find_first_zero_bit(smp->state, cnt); set_bit(blk, ps->pending); set_bit(blk, smp->state); } } else { /* shrink the existing pending reservation: */ for (i = cur_nblks; i > nblks; i--) { int blk = find_first_bit(ps->pending, cnt); clear_bit(blk, ps->pending); /* clear in global smp_state if not in configured * otherwise until _commit() */ if (!test_bit(blk, ps->configured)) clear_bit(blk, smp->state); } } fail: spin_unlock_irqrestore(&smp->state_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
rob clarkrob clark14549.49%112.50%
stephane viaustephane viau12442.32%562.50%
wentao xuwentao xu248.19%225.00%
Total293100.00%8100.00%


static void set_fifo_thresholds(struct mdp5_smp *smp, enum mdp5_pipe pipe, int nblks) { struct mdp5_kms *mdp5_kms = get_kms(smp); u32 smp_entries_per_blk = smp->blk_size / (128 / BITS_PER_BYTE); u32 val; /* 1/4 of SMP pool that is being fetched */ val = (nblks * smp_entries_per_blk) / 4; mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1); mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2); mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3); }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau97100.00%1100.00%
Total97100.00%1100.00%

/* * NOTE: looks like if horizontal decimation is used (if we supported that) * then the width used to calculate SMP block requirements is the post- * decimated width. Ie. SMP buffering sits downstream of decimation (which * presumably happens during the dma from scanout buffer). */
int mdp5_smp_request(struct mdp5_smp *smp, enum mdp5_pipe pipe, const struct mdp_format *format, u32 width, bool hdecim) { struct mdp5_kms *mdp5_kms = get_kms(smp); struct drm_device *dev = mdp5_kms->dev; int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg); int i, hsub, nplanes, nlines, nblks, ret; u32 fmt = format->base.pixel_format; nplanes = drm_format_num_planes(fmt); hsub = drm_format_horz_chroma_subsampling(fmt); /* different if BWC (compressed framebuffer?) enabled: */ nlines = 2; /* Newer MDPs have split/packing logic, which fetches sub-sampled * U and V components (splits them from Y if necessary) and packs * them together, writes to SMP using a single client. */ if ((rev > 0) && (format->chroma_sample > CHROMA_FULL)) { fmt = DRM_FORMAT_NV24; nplanes = 2; /* if decimation is enabled, HW decimates less on the * sub sampled chroma components */ if (hdecim && (hsub > 1)) hsub = 1; } for (i = 0, nblks = 0; i < nplanes; i++) { int n, fetch_stride, cpp; cpp = drm_format_plane_cpp(fmt, i); fetch_stride = width * cpp / (i ? hsub : 1); n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size); /* for hw rev v1.00 */ if (rev == 0) n = roundup_pow_of_two(n); DBG("%s[%d]: request %d SMP blocks", pipe2name(pipe), i, n); ret = smp_request_block(smp, pipe2client(pipe, i), n); if (ret) { dev_err(dev->dev, "Cannot allocate %d SMP blocks: %d\n", n, ret); return ret; } nblks += n; } set_fifo_thresholds(smp, pipe, nblks); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau21977.66%250.00%
wentao xuwentao xu5920.92%125.00%
rob clarkrob clark41.42%125.00%
Total282100.00%4100.00%

/* Release SMP blocks for all clients of the pipe */
void mdp5_smp_release(struct mdp5_smp *smp, enum mdp5_pipe pipe) { int i; unsigned long flags; int cnt = smp->blk_cnt; for (i = 0; i < pipe2nclients(pipe); i++) { mdp5_smp_state_t assigned; u32 cid = pipe2client(pipe, i); struct mdp5_client_smp_state *ps = &smp->client_state[cid]; spin_lock_irqsave(&smp->state_lock, flags); /* clear hw assignment */ bitmap_or(assigned, ps->inuse, ps->configured, cnt); update_smp_state(smp, CID_UNUSED, &assigned); /* free to global pool */ bitmap_andnot(smp->state, smp->state, ps->pending, cnt); bitmap_andnot(smp->state, smp->state, assigned, cnt); /* clear client's infor */ bitmap_zero(ps->pending, cnt); bitmap_zero(ps->configured, cnt); bitmap_zero(ps->inuse, cnt); spin_unlock_irqrestore(&smp->state_lock, flags); } set_fifo_thresholds(smp, pipe, 0); }

Contributors

PersonTokensPropCommitsCommitProp
wentao xuwentao xu13772.87%125.00%
stephane viaustephane viau3920.74%125.00%
rob clarkrob clark126.38%250.00%
Total188100.00%4100.00%


static void update_smp_state(struct mdp5_smp *smp, u32 cid, mdp5_smp_state_t *assigned) { struct mdp5_kms *mdp5_kms = get_kms(smp); int cnt = smp->blk_cnt; u32 blk, val; for_each_set_bit(blk, *assigned, cnt) { int idx = blk / 3; int fld = blk % 3; val = mdp5_read(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx)); switch (fld) { case 0: val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__MASK; val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0(cid); break; case 1: val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__MASK; val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1(cid); break; case 2: val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__MASK; val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2(cid); break; } mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx), val); mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_R_REG(0, idx), val); } }

Contributors

PersonTokensPropCommitsCommitProp
rob clarkrob clark12980.62%125.00%
stephane viaustephane viau3119.38%375.00%
Total160100.00%4100.00%

/* step #2: configure hw for union(pending, inuse): */
void mdp5_smp_configure(struct mdp5_smp *smp, enum mdp5_pipe pipe) { int cnt = smp->blk_cnt; mdp5_smp_state_t assigned; int i; for (i = 0; i < pipe2nclients(pipe); i++) { u32 cid = pipe2client(pipe, i); struct mdp5_client_smp_state *ps = &smp->client_state[cid]; /* * if vblank has not happened since last smp_configure * skip the configure for now */ if (!bitmap_equal(ps->inuse, ps->configured, cnt)) continue; bitmap_copy(ps->configured, ps->pending, cnt); bitmap_or(assigned, ps->inuse, ps->configured, cnt); update_smp_state(smp, cid, &assigned); } }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau4939.52%240.00%
rob clarkrob clark4334.68%240.00%
wentao xuwentao xu3225.81%120.00%
Total124100.00%5100.00%

/* step #3: after vblank, copy configured -> inuse: */
void mdp5_smp_commit(struct mdp5_smp *smp, enum mdp5_pipe pipe) { int cnt = smp->blk_cnt; mdp5_smp_state_t released; int i; for (i = 0; i < pipe2nclients(pipe); i++) { u32 cid = pipe2client(pipe, i); struct mdp5_client_smp_state *ps = &smp->client_state[cid]; /* * Figure out if there are any blocks we where previously * using, which can be released and made available to other * clients: */ if (bitmap_andnot(released, ps->inuse, ps->configured, cnt)) { unsigned long flags; spin_lock_irqsave(&smp->state_lock, flags); /* clear released blocks: */ bitmap_andnot(smp->state, smp->state, released, cnt); spin_unlock_irqrestore(&smp->state_lock, flags); update_smp_state(smp, CID_UNUSED, &released); } bitmap_copy(ps->inuse, ps->configured, cnt); } }

Contributors

PersonTokensPropCommitsCommitProp
rob clarkrob clark8958.94%240.00%
stephane viaustephane viau6039.74%240.00%
wentao xuwentao xu21.32%120.00%
Total151100.00%5100.00%


void mdp5_smp_destroy(struct mdp5_smp *smp) { kfree(smp); }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau1386.67%150.00%
rob clarkrob clark213.33%150.00%
Total15100.00%2100.00%


struct mdp5_smp *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_block *cfg) { struct mdp5_smp *smp = NULL; int ret; smp = kzalloc(sizeof(*smp), GFP_KERNEL); if (unlikely(!smp)) { ret = -ENOMEM; goto fail; } smp->dev = dev; smp->blk_cnt = cfg->mmb_count; smp->blk_size = cfg->mmb_size; /* statically tied MMBs cannot be re-allocated: */ bitmap_copy(smp->state, cfg->reserved_state, smp->blk_cnt); memcpy(smp->reserved, cfg->reserved, sizeof(smp->reserved)); spin_lock_init(&smp->state_lock); return smp; fail: if (smp) mdp5_smp_destroy(smp); return ERR_PTR(ret); }

Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau14198.60%266.67%
rob clarkrob clark21.40%133.33%
Total143100.00%3100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
stephane viaustephane viau89155.62%660.00%
rob clarkrob clark43727.28%220.00%
wentao xuwentao xu27417.10%220.00%
Total1602100.00%10100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}