1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drm kms/fb dma helper functions
*
* Copyright (C) 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Based on udl_fbdev.c
* Copyright (C) 2012 Red Hat
*/
#include <drm/drm_damage_helper.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_plane.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
/**
* DOC: framebuffer dma helper functions
*
* Provides helper functions for creating a DMA-contiguous framebuffer.
*
* Depending on the platform, the buffers may be physically non-contiguous and
* mapped through an IOMMU or a similar mechanism, or allocated from
* physically-contiguous memory (using, for instance, CMA or a pool of memory
* reserved at early boot). This is handled behind the scenes by the DMA mapping
* API.
*
* drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create
* callback function to create a DMA-contiguous framebuffer.
*/
/**
* drm_fb_dma_get_gem_obj() - Get DMA GEM object for framebuffer
* @fb: The framebuffer
* @plane: Which plane
*
* Return the DMA GEM object for given framebuffer.
*
* This function will usually be called from the CRTC callback functions.
*/
struct drm_gem_dma_object *drm_fb_dma_get_gem_obj(struct drm_framebuffer *fb,
unsigned int plane)
{
struct drm_gem_object *gem;
gem = drm_gem_fb_get_obj(fb, plane);
if (!gem)
return NULL;
return to_drm_gem_dma_obj(gem);
}
EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_obj);
/**
* drm_fb_dma_get_gem_addr() - Get DMA (bus) address for framebuffer, for pixel
* formats where values are grouped in blocks this will get you the beginning of
* the block
* @fb: The framebuffer
* @state: Which state of drm plane
* @plane: Which plane
* Return the DMA GEM address for given framebuffer.
*
* This function will usually be called from the PLANE callback functions.
*/
dma_addr_t drm_fb_dma_get_gem_addr(struct drm_framebuffer *fb,
struct drm_plane_state *state,
unsigned int plane)
{
struct drm_gem_dma_object *obj;
dma_addr_t dma_addr;
u8 h_div = 1, v_div = 1;
u32 block_w = drm_format_info_block_width(fb->format, plane);
u32 block_h = drm_format_info_block_height(fb->format, plane);
u32 block_size = fb->format->char_per_block[plane];
u32 sample_x;
u32 sample_y;
u32 block_start_y;
u32 num_hblocks;
obj = drm_fb_dma_get_gem_obj(fb, plane);
if (!obj)
return 0;
dma_addr = obj->dma_addr + fb->offsets[plane];
if (plane > 0) {
h_div = fb->format->hsub;
v_div = fb->format->vsub;
}
sample_x = (state->src_x >> 16) / h_div;
sample_y = (state->src_y >> 16) / v_div;
block_start_y = (sample_y / block_h) * block_h;
num_hblocks = sample_x / block_w;
dma_addr += fb->pitches[plane] * block_start_y;
dma_addr += block_size * num_hblocks;
return dma_addr;
}
EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_addr);
/**
* drm_fb_dma_sync_non_coherent - Sync GEM object to non-coherent backing
* memory
* @drm: DRM device
* @old_state: Old plane state
* @state: New plane state
*
* This function can be used by drivers that use damage clips and have
* DMA GEM objects backed by non-coherent memory. Calling this function
* in a plane's .atomic_update ensures that all the data in the backing
* memory have been written to RAM.
*/
void drm_fb_dma_sync_non_coherent(struct drm_device *drm,
struct drm_plane_state *old_state,
struct drm_plane_state *state)
{
const struct drm_format_info *finfo = state->fb->format;
struct drm_atomic_helper_damage_iter iter;
const struct drm_gem_dma_object *dma_obj;
unsigned int offset, i;
struct drm_rect clip;
dma_addr_t daddr;
size_t nb_bytes;
for (i = 0; i < finfo->num_planes; i++) {
dma_obj = drm_fb_dma_get_gem_obj(state->fb, i);
if (!dma_obj->map_noncoherent)
continue;
daddr = drm_fb_dma_get_gem_addr(state->fb, state, i);
drm_atomic_helper_damage_iter_init(&iter, old_state, state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
/* Ignore x1/x2 values, invalidate complete lines */
offset = clip.y1 * state->fb->pitches[i];
nb_bytes = (clip.y2 - clip.y1) * state->fb->pitches[i];
dma_sync_single_for_device(drm->dev, daddr + offset,
nb_bytes, DMA_TO_DEVICE);
}
}
}
EXPORT_SYMBOL_GPL(drm_fb_dma_sync_non_coherent);
|