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