// SPDX-License-Identifier: GPL-2.0-or-later1/*2* drm kms/fb dma helper functions3*4* Copyright (C) 2012 Analog Devices Inc.5* Author: Lars-Peter Clausen <[email protected]>6*7* Based on udl_fbdev.c8* Copyright (C) 2012 Red Hat9*/1011#include <drm/drm_damage_helper.h>12#include <drm/drm_fb_dma_helper.h>13#include <drm/drm_fourcc.h>14#include <drm/drm_framebuffer.h>15#include <drm/drm_gem_dma_helper.h>16#include <drm/drm_gem_framebuffer_helper.h>17#include <drm/drm_panic.h>18#include <drm/drm_plane.h>1920#include <linux/dma-mapping.h>21#include <linux/export.h>22#include <linux/module.h>2324/**25* DOC: framebuffer dma helper functions26*27* Provides helper functions for creating a DMA-contiguous framebuffer.28*29* Depending on the platform, the buffers may be physically non-contiguous and30* mapped through an IOMMU or a similar mechanism, or allocated from31* physically-contiguous memory (using, for instance, CMA or a pool of memory32* reserved at early boot). This is handled behind the scenes by the DMA mapping33* API.34*35* drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create36* callback function to create a DMA-contiguous framebuffer.37*/3839/**40* drm_fb_dma_get_gem_obj() - Get DMA GEM object for framebuffer41* @fb: The framebuffer42* @plane: Which plane43*44* Return the DMA GEM object for given framebuffer.45*46* This function will usually be called from the CRTC callback functions.47*/48struct drm_gem_dma_object *drm_fb_dma_get_gem_obj(struct drm_framebuffer *fb,49unsigned int plane)50{51struct drm_gem_object *gem;5253gem = drm_gem_fb_get_obj(fb, plane);54if (!gem)55return NULL;5657return to_drm_gem_dma_obj(gem);58}59EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_obj);6061/**62* drm_fb_dma_get_gem_addr() - Get DMA (bus) address for framebuffer, for pixel63* formats where values are grouped in blocks this will get you the beginning of64* the block65* @fb: The framebuffer66* @state: Which state of drm plane67* @plane: Which plane68* Return the DMA GEM address for given framebuffer.69*70* This function will usually be called from the PLANE callback functions.71*/72dma_addr_t drm_fb_dma_get_gem_addr(struct drm_framebuffer *fb,73struct drm_plane_state *state,74unsigned int plane)75{76struct drm_gem_dma_object *obj;77dma_addr_t dma_addr;78u8 h_div = 1, v_div = 1;79u32 block_w = drm_format_info_block_width(fb->format, plane);80u32 block_h = drm_format_info_block_height(fb->format, plane);81u32 block_size = fb->format->char_per_block[plane];82u32 sample_x;83u32 sample_y;84u32 block_start_y;85u32 num_hblocks;8687obj = drm_fb_dma_get_gem_obj(fb, plane);88if (!obj)89return 0;9091dma_addr = obj->dma_addr + fb->offsets[plane];9293if (plane > 0) {94h_div = fb->format->hsub;95v_div = fb->format->vsub;96}9798sample_x = (state->src_x >> 16) / h_div;99sample_y = (state->src_y >> 16) / v_div;100block_start_y = (sample_y / block_h) * block_h;101num_hblocks = sample_x / block_w;102103dma_addr += fb->pitches[plane] * block_start_y;104dma_addr += block_size * num_hblocks;105106return dma_addr;107}108EXPORT_SYMBOL_GPL(drm_fb_dma_get_gem_addr);109110/**111* drm_fb_dma_sync_non_coherent - Sync GEM object to non-coherent backing112* memory113* @drm: DRM device114* @old_state: Old plane state115* @state: New plane state116*117* This function can be used by drivers that use damage clips and have118* DMA GEM objects backed by non-coherent memory. Calling this function119* in a plane's .atomic_update ensures that all the data in the backing120* memory have been written to RAM.121*/122void drm_fb_dma_sync_non_coherent(struct drm_device *drm,123struct drm_plane_state *old_state,124struct drm_plane_state *state)125{126const struct drm_format_info *finfo = state->fb->format;127struct drm_atomic_helper_damage_iter iter;128const struct drm_gem_dma_object *dma_obj;129unsigned int offset, i;130struct drm_rect clip;131dma_addr_t daddr;132size_t nb_bytes;133134for (i = 0; i < finfo->num_planes; i++) {135dma_obj = drm_fb_dma_get_gem_obj(state->fb, i);136if (!dma_obj->map_noncoherent)137continue;138139daddr = drm_fb_dma_get_gem_addr(state->fb, state, i);140drm_atomic_helper_damage_iter_init(&iter, old_state, state);141142drm_atomic_for_each_plane_damage(&iter, &clip) {143/* Ignore x1/x2 values, invalidate complete lines */144offset = clip.y1 * state->fb->pitches[i];145146nb_bytes = (clip.y2 - clip.y1) * state->fb->pitches[i];147dma_sync_single_for_device(drm->dev, daddr + offset,148nb_bytes, DMA_TO_DEVICE);149}150}151}152EXPORT_SYMBOL_GPL(drm_fb_dma_sync_non_coherent);153154/**155* drm_fb_dma_get_scanout_buffer - Provide a scanout buffer in case of panic156* @plane: DRM primary plane157* @sb: scanout buffer for the panic handler158* Returns: 0 or negative error code159*160* Generic get_scanout_buffer() implementation, for drivers that uses the161* drm_fb_dma_helper. It won't call vmap in the panic context, so the driver162* should make sure the primary plane is vmapped, otherwise the panic screen163* won't get displayed.164*/165int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane,166struct drm_scanout_buffer *sb)167{168struct drm_gem_dma_object *dma_obj;169struct drm_framebuffer *fb;170171if (!plane->state || !plane->state->fb)172return -EINVAL;173174fb = plane->state->fb;175/* Only support linear modifier */176if (fb->modifier != DRM_FORMAT_MOD_LINEAR)177return -ENODEV;178179dma_obj = drm_fb_dma_get_gem_obj(fb, 0);180181/* Buffer should be accessible from the CPU */182if (drm_gem_is_imported(&dma_obj->base))183return -ENODEV;184185/* Buffer should be already mapped to CPU */186if (!dma_obj->vaddr)187return -ENODEV;188189iosys_map_set_vaddr(&sb->map[0], dma_obj->vaddr);190sb->format = fb->format;191sb->height = fb->height;192sb->width = fb->width;193sb->pitch[0] = fb->pitches[0];194return 0;195}196EXPORT_SYMBOL(drm_fb_dma_get_scanout_buffer);197198199