|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * drm kms/fb cma (contiguous memory allocator) 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_cma_helper.h> | 
|  | #include <drm/drm_fourcc.h> | 
|  | #include <drm/drm_framebuffer.h> | 
|  | #include <drm/drm_gem_cma_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 cma helper functions | 
|  | * | 
|  | * Provides helper functions for creating a cma (contiguous memory allocator) | 
|  | * backed framebuffer. | 
|  | * | 
|  | * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create | 
|  | * callback function to create a cma backed framebuffer. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer | 
|  | * @fb: The framebuffer | 
|  | * @plane: Which plane | 
|  | * | 
|  | * Return the CMA GEM object for given framebuffer. | 
|  | * | 
|  | * This function will usually be called from the CRTC callback functions. | 
|  | */ | 
|  | struct drm_gem_cma_object *drm_fb_cma_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_cma_obj(gem); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); | 
|  |  | 
|  | /** | 
|  | * drm_fb_cma_get_gem_addr() - Get physical 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 CMA GEM address for given framebuffer. | 
|  | * | 
|  | * This function will usually be called from the PLANE callback functions. | 
|  | */ | 
|  | dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, | 
|  | struct drm_plane_state *state, | 
|  | unsigned int plane) | 
|  | { | 
|  | struct drm_gem_cma_object *obj; | 
|  | dma_addr_t paddr; | 
|  | 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_cma_get_gem_obj(fb, plane); | 
|  | if (!obj) | 
|  | return 0; | 
|  |  | 
|  | paddr = obj->paddr + 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; | 
|  |  | 
|  | paddr += fb->pitches[plane] * block_start_y; | 
|  | paddr += block_size * num_hblocks; | 
|  |  | 
|  | return paddr; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr); | 
|  |  | 
|  | /** | 
|  | * drm_fb_cma_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 | 
|  | * CMA 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_cma_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_cma_object *cma_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++) { | 
|  | cma_obj = drm_fb_cma_get_gem_obj(state->fb, i); | 
|  | if (!cma_obj->map_noncoherent) | 
|  | continue; | 
|  |  | 
|  | daddr = drm_fb_cma_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_cma_sync_non_coherent); |