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_panic.h> 19 #include <drm/drm_plane.h> 20 21 #include <linux/dma-mapping.h> 22 #include <linux/export.h> 23 #include <linux/module.h> 24 25 /** 26 * DOC: framebuffer dma helper functions 27 * 28 * Provides helper functions for creating a DMA-contiguous framebuffer. 29 * 30 * Depending on the platform, the buffers may be physically non-contiguous and 31 * mapped through an IOMMU or a similar mechanism, or allocated from 32 * physically-contiguous memory (using, for instance, CMA or a pool of memory 33 * reserved at early boot). This is handled behind the scenes by the DMA mapping 34 * API. 35 * 36 * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create 37 * callback function to create a DMA-contiguous framebuffer. 38 */ 39 40 /** 41 * drm_fb_dma_get_gem_obj() - Get DMA GEM object for framebuffer 42 * @fb: The framebuffer 43 * @plane: Which plane 44 * 45 * Return the DMA GEM object for given framebuffer. 46 * 47 * This function will usually be called from the CRTC callback functions. 48 */ 49 struct drm_gem_dma_object *drm_fb_dma_get_gem_obj(struct drm_framebuffer *fb, 50 unsigned int plane) 51 { 52 struct drm_gem_object *gem; 53 54 gem = drm_gem_fb_get_obj(fb, plane); 55 if (!gem) 56 return NULL; 57 58 return to_drm_gem_dma_obj(gem); 59 } 60 EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_obj); 61 62 /** 63 * drm_fb_dma_get_gem_addr() - Get DMA (bus) address for framebuffer, for pixel 64 * formats where values are grouped in blocks this will get you the beginning of 65 * the block 66 * @fb: The framebuffer 67 * @state: Which state of drm plane 68 * @plane: Which plane 69 * Return the DMA GEM address for given framebuffer. 70 * 71 * This function will usually be called from the PLANE callback functions. 72 */ 73 dma_addr_t drm_fb_dma_get_gem_addr(struct drm_framebuffer *fb, 74 struct drm_plane_state *state, 75 unsigned int plane) 76 { 77 struct drm_gem_dma_object *obj; 78 dma_addr_t dma_addr; 79 u8 h_div = 1, v_div = 1; 80 u32 block_w = drm_format_info_block_width(fb->format, plane); 81 u32 block_h = drm_format_info_block_height(fb->format, plane); 82 u32 block_size = fb->format->char_per_block[plane]; 83 u32 sample_x; 84 u32 sample_y; 85 u32 block_start_y; 86 u32 num_hblocks; 87 88 obj = drm_fb_dma_get_gem_obj(fb, plane); 89 if (!obj) 90 return 0; 91 92 dma_addr = obj->dma_addr + fb->offsets[plane]; 93 94 if (plane > 0) { 95 h_div = fb->format->hsub; 96 v_div = fb->format->vsub; 97 } 98 99 sample_x = (state->src_x >> 16) / h_div; 100 sample_y = (state->src_y >> 16) / v_div; 101 block_start_y = (sample_y / block_h) * block_h; 102 num_hblocks = sample_x / block_w; 103 104 dma_addr += fb->pitches[plane] * block_start_y; 105 dma_addr += block_size * num_hblocks; 106 107 return dma_addr; 108 } 109 EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_addr); 110 111 /** 112 * drm_fb_dma_sync_non_coherent - Sync GEM object to non-coherent backing 113 * memory 114 * @drm: DRM device 115 * @old_state: Old plane state 116 * @state: New plane state 117 * 118 * This function can be used by drivers that use damage clips and have 119 * DMA GEM objects backed by non-coherent memory. Calling this function 120 * in a plane's .atomic_update ensures that all the data in the backing 121 * memory have been written to RAM. 122 */ 123 void drm_fb_dma_sync_non_coherent(struct drm_device *drm, 124 struct drm_plane_state *old_state, 125 struct drm_plane_state *state) 126 { 127 const struct drm_format_info *finfo = state->fb->format; 128 struct drm_atomic_helper_damage_iter iter; 129 const struct drm_gem_dma_object *dma_obj; 130 unsigned int offset, i; 131 struct drm_rect clip; 132 dma_addr_t daddr; 133 size_t nb_bytes; 134 135 for (i = 0; i < finfo->num_planes; i++) { 136 dma_obj = drm_fb_dma_get_gem_obj(state->fb, i); 137 if (!dma_obj->map_noncoherent) 138 continue; 139 140 daddr = drm_fb_dma_get_gem_addr(state->fb, state, i); 141 drm_atomic_helper_damage_iter_init(&iter, old_state, state); 142 143 drm_atomic_for_each_plane_damage(&iter, &clip) { 144 /* Ignore x1/x2 values, invalidate complete lines */ 145 offset = clip.y1 * state->fb->pitches[i]; 146 147 nb_bytes = (clip.y2 - clip.y1) * state->fb->pitches[i]; 148 dma_sync_single_for_device(drm->dev, daddr + offset, 149 nb_bytes, DMA_TO_DEVICE); 150 } 151 } 152 } 153 EXPORT_SYMBOL_GPL(drm_fb_dma_sync_non_coherent); 154 155 /** 156 * drm_fb_dma_get_scanout_buffer - Provide a scanout buffer in case of panic 157 * @plane: DRM primary plane 158 * @sb: scanout buffer for the panic handler 159 * Returns: 0 or negative error code 160 * 161 * Generic get_scanout_buffer() implementation, for drivers that uses the 162 * drm_fb_dma_helper. It won't call vmap in the panic context, so the driver 163 * should make sure the primary plane is vmapped, otherwise the panic screen 164 * won't get displayed. 165 */ 166 int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane, 167 struct drm_scanout_buffer *sb) 168 { 169 struct drm_gem_dma_object *dma_obj; 170 struct drm_framebuffer *fb; 171 172 if (!plane->state || !plane->state->fb) 173 return -EINVAL; 174 175 fb = plane->state->fb; 176 /* Only support linear modifier */ 177 if (fb->modifier != DRM_FORMAT_MOD_LINEAR) 178 return -ENODEV; 179 180 dma_obj = drm_fb_dma_get_gem_obj(fb, 0); 181 182 /* Buffer should be accessible from the CPU */ 183 if (drm_gem_is_imported(&dma_obj->base)) 184 return -ENODEV; 185 186 /* Buffer should be already mapped to CPU */ 187 if (!dma_obj->vaddr) 188 return -ENODEV; 189 190 iosys_map_set_vaddr(&sb->map[0], dma_obj->vaddr); 191 sb->format = fb->format; 192 sb->height = fb->height; 193 sb->width = fb->width; 194 sb->pitch[0] = fb->pitches[0]; 195 return 0; 196 } 197 EXPORT_SYMBOL(drm_fb_dma_get_scanout_buffer); 198