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 */
46struct 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}
57EXPORT_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 */
70dma_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(info: fb->format, plane);
78 u32 block_h = drm_format_info_block_height(info: 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}
106EXPORT_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 */
120void 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: &iter, old_state, new_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(dev: drm->dev, addr: daddr + offset,
146 size: nb_bytes, dir: DMA_TO_DEVICE);
147 }
148 }
149}
150EXPORT_SYMBOL_GPL(drm_fb_dma_sync_non_coherent);
151

source code of linux/drivers/gpu/drm/drm_fb_dma_helper.c