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