Files
luban-lite-t3e-pro/doc/topics/sdk/display/display_mem_managment.html
2025-01-23 16:37:00 +08:00

260 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-cn" lang="zh-cn" data-whc_version="26.0">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="description" content="CMA Linux-3.5 引入了一套 Contiguous Memory Allocator简称 CMA基于 DMA 映射框架为内核提供连续大块内存的申请和释放。CMA 主要思路是将预留内存纳入 DMA 映射管理,可以给系统内所有设备共享使用,这样就既解决了为 GPU、Camera、显示等图像处理类模块预留大块的连续内存又解决了预留内存被空置的问题提升内存使用率。 CMA ..."/><meta name="DC.rights.owner" content="(C) 版权 2025"/><meta name="copyright" content="(C) 版权 2025"/><meta name="generator" content="DITA-OT"/><meta name="DC.type" content="concept"/><meta name="DC.contributor" content="yan.wang"/><meta name="DC.date.modified" content="2024-12-04"/><meta name="DC.format" content="HTML5"/><meta name="DC.identifier" content="display_preserve_memory_managment_process"/><meta name="DC.language" content="zh-CN"/><title>预留内存管理流程</title><!-- Build number 2023110923. --><meta name="wh-path2root" content="../../../"/><meta name="wh-toc-id" content=""/><meta name="wh-source-relpath" content="topics/sdk/display/display_mem_managment.dita"/><meta name="wh-out-relpath" content="topics/sdk/display/display_mem_managment.html"/>
<link rel="stylesheet" type="text/css" href="../../../webhelp/app/commons.css?buildId=2023110923"/>
<link rel="stylesheet" type="text/css" href="../../../webhelp/app/topic.css?buildId=2023110923"/>
<script src="../../../webhelp/app/options/properties.js?buildId=20250121171154"></script>
<script src="../../../webhelp/app/localization/strings.js?buildId=2023110923"></script>
<script src="../../../webhelp/app/search/index/keywords.js?buildId=20250121171154"></script>
<script defer="defer" src="../../../webhelp/app/commons.js?buildId=2023110923"></script>
<script defer="defer" src="../../../webhelp/app/topic.js?buildId=2023110923"></script>
<link rel="stylesheet" type="text/css" href="../../../webhelp/template/aic-styles-web.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/notes.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/aic-common.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/aic-images.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/footnote.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/aic-web-watermark.css?buildId=2023110923"/><link rel="stylesheet" type="text/css" href="../../../webhelp/template/topic-body-list.css?buildId=2023110923"/></head>
<body id="display_preserve_memory_managment_process" class="wh_topic_page frmBody">
<a href="#wh_topic_body" class="sr-only sr-only-focusable">
跳转到主要内容
</a>
<header class="navbar navbar-default wh_header">
<div class="container-fluid">
<div class="wh_header_flex_container navbar-nav navbar-expand-md navbar-dark">
<div class="wh_logo_and_publication_title_container">
<div class="wh_logo_and_publication_title">
<a href="http://www.artinchip.com" class=" wh_logo d-none d-sm-block "><img src="../../../company-logo-white.png" alt="RTOS SDK 使用指南SDK 指南文件"/></a>
<div class=" wh_publication_title "><a href="../../../index.html"><span class="booktitle"><span class="ph mainbooktitle">RTOS SDK 使用指南</span><span class="ph booktitlealt">SDK 指南文件</span></span></a></div>
</div>
</div>
<div class="wh_top_menu_and_indexterms_link collapse navbar-collapse" id="wh_top_menu_and_indexterms_link">
</div>
</div>
</div>
</header>
<div class=" wh_search_input navbar-form wh_topic_page_search search " role="form">
<form id="searchForm" method="get" role="search" action="../../../search.html"><div><input type="search" placeholder="搜索 " class="wh_search_textfield" id="textToSearch" name="searchQuery" aria-label="搜索查询" required="required"/><button type="submit" class="wh_search_button" aria-label="搜索"><span class="search_input_text">搜索</span></button></div></form>
</div>
<div class="container-fluid" id="wh_topic_container">
<div class="row">
<nav class="wh_tools d-print-none navbar-expand-md" aria-label="Tools">
<div data-tooltip-position="bottom" class=" wh_breadcrumb "></div>
<div class="wh_right_tools">
<button class="wh_hide_highlight" aria-label="切换搜索突出显示" title="切换搜索突出显示"></button>
<button class="webhelp_expand_collapse_sections" data-next-state="collapsed" aria-label="折叠截面" title="折叠截面"></button>
<div class=" wh_print_link print d-none d-md-inline-block "><button onClick="window.print()" title="打印此页" aria-label="打印此页"></button></div>
</div>
</nav>
</div>
<div class="wh_content_area">
<div class="row">
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12" id="wh_topic_body">
<button id="wh_close_topic_toc_button" class="close-toc-button d-none" aria-label="Toggle topic table of content" aria-controls="wh_topic_toc" aria-expanded="true">
<span class="close-toc-icon-container">
<span class="close-toc-icon"></span>
</span>
</button>
<div class=" wh_topic_content body "><main role="main"><article class="- topic/topic concept/concept topic concept" role="article" aria-labelledby="ariaid-title1"><span class="edit-link" style="font-size:12px; opacity:0.6; text-align:right; vertical-align:middle"><a target="_blank" href="http://172.16.35.88/tasks/jdssno1uvvbf2mltu9kb9v3if05d5gopuakboe8hlud18rma/edit/F:/aicdita/aicdita-cn/topics/sdk/display/display_mem_managment.dita">Edit online</a></span><h1 class="- topic/title title topictitle1" id="ariaid-title1">预留内存管理流程</h1><div class="date inPage">4 Dec 2024</div><div style="color: gray;">
Read time: 4 minute(s)
</div><div class="- topic/body concept/conbody body conbody"><section class="- topic/section section" id="display_preserve_memory_managment_process__section_w5d_21g_d1c" data-ofbid="display_preserve_memory_managment_process__section_w5d_21g_d1c"><h2 class="- topic/title title sectiontitle">CMA</h2><p class="- topic/p p" data-ofbid="d313808e25__20250121171819">Linux-3.5 引入了一套 Contiguous Memory
Allocator简称 CMA基于 DMA 映射框架为内核提供连续大块内存的申请和释放。CMA 主要思路是将预留内存纳入 DMA
映射管理,可以给系统内所有设备共享使用,这样就既解决了为
GPU、Camera、显示等图像处理类模块预留大块的连续内存又解决了预留内存被空置的问题提升内存使用率。</p><p class="- topic/p p" data-ofbid="d313808e27__20250121171819">CMA
本身不是一套分配内存的算法,它的底层仍然要依赖伙伴算法系统来支持,可以理解为 CMA 是介于 DMA mapping 和内存管理之间的中间层。</p><div class="- topic/p p" data-ofbid="d313808e29__20250121171819"><br/><div class="imagecenter"><img class="- topic/image image imagecenter" id="display_preserve_memory_managment_process__image_x5d_21g_d1c" src="../../../images/display/display_lb_cma.png" width="384" alt="cma"/></div><br/></div><p class="- topic/p p" data-ofbid="d313808e33__20250121171819">CMA 的具体功能有:</p><ol class="- topic/ol ol" id="display_preserve_memory_managment_process__ol_y5d_21g_d1c" data-ofbid="display_preserve_memory_managment_process__ol_y5d_21g_d1c"><li class="- topic/li li" data-ofbid="d313808e36__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e38__20250121171819">在系统的启动过程中,根据内核编译配置、或者 DTS 配置将内存中某块区域用于 CMA然后内核中其他模块可以通过 DMA 的接口 API
申请连续内存,这块区域我们称之为 CMA area。</p>
</li><li class="- topic/li li" data-ofbid="d313808e41__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e43__20250121171819">提供 cma_alloc()和 cma_release()两个接口函数用于分配和释放 CMA pages。</p>
</li><li class="- topic/li li" data-ofbid="d313808e46__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e48__20250121171819">记录和跟踪 CMA area 中各个 pages 的状态。</p>
</li><li class="- topic/li li" data-ofbid="d313808e51__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e53__20250121171819">调用伙伴系统接口,进行真正的内存分配。</p>
</li></ol><p class="- topic/p p" data-ofbid="d313808e56__20250121171819"><strong class="+ topic/ph hi-d/b ph b">CMA 主要接口</strong></p><ol class="- topic/ol ol" id="display_preserve_memory_managment_process__ol_z5d_21g_d1c" data-ofbid="display_preserve_memory_managment_process__ol_z5d_21g_d1c"><li class="- topic/li li" data-ofbid="d313808e60__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e62__20250121171819">CNA area 的声明</p>
<div class="- topic/p p" data-ofbid="d313808e65__20250121171819">setup_bootmem() -&gt; dma_contiguous_reserve(),定义在
<span class="+ topic/ph sw-d/filepath ph filepath">kernel/dma/contiguous.c</span>,其中有确定 CMA area size
的代码如下:<pre class="+ topic/pre pr-d/codeblock pre codeblock language-c" id="display_preserve_memory_managment_process__codeblock_yvp_g1g_d1c" data-ofbid="display_preserve_memory_managment_process__codeblock_yvp_g1g_d1c">#ifdef CONFIG_CMA_SIZE_SEL_MBYTES
selected_size = size_bytes;
#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
selected_size = cma_early_percent_memory();
#elif defined(CONFIG_CMA_SIZE_SEL_MIN)
selected_size = min(size_bytes, cma_early_percent_memory());
#elif defined(CONFIG_CMA_SIZE_SEL_MAX)
selected_size = max(size_bytes, cma_early_percent_memory());
#endif</pre></div>
<p class="- topic/p p" data-ofbid="d313808e73__20250121171819">然后会调用 dma_contiguous_reserve_area() -&gt; cma_declare_contiguous() 去初始化 CMA
的配置参数。</p>
<div class="- topic/note note note note_note" id="display_preserve_memory_managment_process__note_xhx_g1g_d1c" data-ofbid="display_preserve_memory_managment_process__note_xhx_g1g_d1c"><span class="note__title">注:</span>
<p class="- topic/p p" data-ofbid="d313808e78__20250121171819">计算 CMA 内存大小的过程中size_bytes 来源于内核编译配置中 CONFIG_CMA_SIZE_MBYTES通过计算将 size
限定在 4 MB 对齐。</p>
</div>
</li><li class="- topic/li li" data-ofbid="d313808e82__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e84__20250121171819">CMA 初始化</p>
<p class="- topic/p p" data-ofbid="d313808e87__20250121171819"><span class="+ topic/ph sw-d/filepath ph filepath">mm/cma.c</span> 中的 cma_init_reserved_areas() -&gt;
init_cma_reserved_pageblock(),其中会设置 page 属性为 MIGRATE_CMA。</p>
</li><li class="- topic/li li" data-ofbid="d313808e93__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e95__20250121171819">申请 CMA</p>
<p class="- topic/p p" data-ofbid="d313808e98__20250121171819">使用 DMA 标准接口 dma_alloc_coherent() 和 dma_alloc_wc(),会间接调用
dma_alloc_from_contiguous()。</p>
</li><li class="- topic/li li" data-ofbid="d313808e101__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e103__20250121171819">释放 CMA</p>
<p class="- topic/p p" data-ofbid="d313808e106__20250121171819">使用 DMA 标准接口 dma_free_coherent()。</p>
</li></ol></section><section class="- topic/section section" id="display_preserve_memory_managment_process__section_gvd_21g_d1c" data-ofbid="display_preserve_memory_managment_process__section_gvd_21g_d1c"><h2 class="- topic/title title sectiontitle">DMA-BUF</h2><p class="- topic/p p" data-ofbid="d313808e112__20250121171819">CMA
解决的是预留内存空闲期间如何给其他设备共享的问题DMA-BUF 解决的是使用期间多个设备共享的问题、以及内核态和用户态如何共享内存的问题。DMA-BUF
可减少多余的拷贝,提升系统运行效率。</p><p class="- topic/p p" data-ofbid="d313808e114__20250121171819">DMA-BUF 最初原型是 hrbuf于 2011 年首次提出,实现了 “Buffer Sharing”
的概念验证。shrbuf 被社区重构变身为 DMA-BUF2012 年合入 Linux-3.3 主线版本。</p><p class="- topic/p p" data-ofbid="d313808e116__20250121171819">DMA-BUF 被广泛用在多媒体驱动中,尤其在
V4L2、DRM 子系统中经常用到。</p><p class="- topic/p p" data-ofbid="d313808e118__20250121171819"><strong class="+ topic/ph hi-d/b ph b">DMA-BUF vs ION</strong></p><p class="- topic/p p" data-ofbid="d313808e121__20250121171819">从 Linux-5.6 开始DMA-BUF 正式合入了原来 ION 的 heap 管理功能,社区的主分支是打算抛弃 ION 了。在 Linux-5.11,主分支已经删除了
<span class="+ topic/ph sw-d/filepath ph filepath">drivers/staging/android/ion</span> 代码,原因是“原厂对 ION
的支持在社区中不太活跃,很难持续更新 ION”而且 ION 还带来了几个 break。</p><ol class="- topic/ol ol" id="display_preserve_memory_managment_process__ol_hvd_21g_d1c" data-ofbid="display_preserve_memory_managment_process__ol_hvd_21g_d1c"><li class="- topic/li li" data-ofbid="d313808e127__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e129__20250121171819">ION 有大量的 heap 逻辑管理,而 DMA-BUF 的 heap 更多是分配接口上的管理(社区更容易接受)。</p>
</li><li class="- topic/li li" data-ofbid="d313808e132__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e134__20250121171819">ION 是生产一个字符设备 /dev/ion而 DMA-BUF 为每个 heap 生成一个字符设备,便于用户态的 heap 区分和权限管理。</p>
</li><li class="- topic/li li" data-ofbid="d313808e137__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e139__20250121171819">ION 限制最多 32 个 heapDMA-BUF 没有这个限制。</p>
</li><li class="- topic/li li" data-ofbid="d313808e142__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e144__20250121171819">DMA-BUF 中实现了两个初始的 heapsystem heap 和 cma heap与原 ION 中的功能类似,但做了很大简化(为了方便社区
review删掉原来 ION 中针对 system、cma 做过的一些优化,涉及 uncached buffers、large page
allocation、page pooling 和 freeing。 DMA-BUF 中的 CMA只添加了 CMA
区域。原 ION 中的 CMA 是添加了所有 DMA 区域。</p>
</li></ol><p class="- topic/p p" data-ofbid="d313808e147__20250121171819"><strong class="+ topic/ph hi-d/b ph b">工作机制</strong></p><p class="- topic/p p" data-ofbid="d313808e150__20250121171819">为了解决各个驱动之间的 buffer 共享问题DMA-BUF 将 buffer 与 file 结合使用,让 DMA-BUF
既是一块物理 buffer同时也是个 Linux 标准 file。典型的应用框图如下</p><div class="- topic/p p" data-ofbid="d313808e152__20250121171819"><br/><div class="imagecenter"><img class="- topic/image image imagecenter" id="display_preserve_memory_managment_process__image_ivd_21g_d1c" src="../../../images/display/dma_buf.png" width="384" alt="dma_buf1"/></div><br/></div><p class="- topic/p p" data-ofbid="d313808e156__20250121171819">分配 buffer 的模块为 exporter使用该 buffer 的模块为 importer。</p><p class="- topic/p p" data-ofbid="d313808e159__20250121171819">DMA-BUF
支持连续物理内存、散列物理内存的 buffer 管理。ArtInChip 平台目前只支持连续物理内存的 DMA-BUF。</p><div class="- topic/p p" data-ofbid="d313808e161__20250121171819"><strong class="+ topic/ph hi-d/b ph b">主要接口</strong><ul class="- topic/ul ul" id="display_preserve_memory_managment_process__ul_pln_41g_d1c" data-ofbid="display_preserve_memory_managment_process__ul_pln_41g_d1c"><li class="- topic/li li" data-ofbid="d313808e165__20250121171819">内核空间<ul class="- topic/ul ul" id="display_preserve_memory_managment_process__ul_qxn_n1g_d1c" data-ofbid="display_preserve_memory_managment_process__ul_qxn_n1g_d1c"><li class="- topic/li li" data-ofbid="d313808e168__20250121171819">作为 exporter<p class="- topic/p p" data-ofbid="d313808e170__20250121171819">注册接口 <span class="+ topic/keyword pr-d/apiname keyword apiname">dma_buf_export()</span></p></li><li class="- topic/li li" data-ofbid="d313808e174__20250121171819">作为
importer<p class="- topic/p p" data-ofbid="d313808e176__20250121171819">获取 buf 的接口:<span class="+ topic/keyword pr-d/apiname keyword apiname">dma_buf_get</span>,``dma_buf_attach()``,``dma_buf_map_attachment``。</p></li></ul></li><li class="- topic/li li" data-ofbid="d313808e181__20250121171819">
<p class="- topic/p p" data-ofbid="d313808e183__20250121171819">用户空间</p>
<p class="- topic/p p" data-ofbid="d313808e186__20250121171819">通过 ioctl 来管理 DMA-BUF。</p>
</li></ul></div><p class="- topic/p p" data-ofbid="d313808e189__20250121171819"><strong class="+ topic/ph hi-d/b ph b">基于 DMA-BUF 的 Video Layer buffer 管理</strong></p><p class="- topic/p p" data-ofbid="d313808e192__20250121171819">UI Layer 的 buffer
driver 申请,通过 /dev/fb0 透传到用户态,用户态使用 mmap() 实现 buffer 共享。</p><p class="- topic/p p" data-ofbid="d313808e194__20250121171819">但对于 Video Layer 的
buffer情况有所不同申请多大 buffer、多少个 buffer 都是由应用场景确定,所以应该是用户态发起申请。同时这个 buffer
还要满足物理连续的特性DE 硬件才能用来做图层叠加处理。</p><div class="- topic/p p" data-ofbid="d313808e196__20250121171819">
<br/><div class="imagecenter"><img class="- topic/image image imagecenter" id="display_preserve_memory_managment_process__image_mvd_21g_d1c" src="../../../images/display/dma_buf_heap.png" width="288" alt="dma_buf_heap"/></div><br/>
</div><p class="- topic/p p" data-ofbid="d313808e202__20250121171819">左图,是 ION 时代的使用方法,新版 LinuxLinux-5.6 以后)的 DMA-BUF 已经支持了 heap 功能,从功能上完全可以替代
ION。对用户态来说看到的差别不再是单一的 /dev/ion 设备节点,而是有多个 /dev/* 设备节点,比如 CMA heap 会生成
/dev/dma_heap/reservedSystem heap 会生成 /dev/dma_heap/system。</p><p class="- topic/p p" data-ofbid="d313808e204__20250121171819">使用 DMA-BUF
的情况下APP 和 fb 驱动共享 buffer 的初始化流程如下图所示:</p><div class="- topic/p p" data-ofbid="d313808e206__20250121171819"><br/><div class="imagecenter"><img class="- topic/image image imagecenter" id="display_preserve_memory_managment_process__image_nvd_21g_d1c" src="../../../images/display/dma_buf_app.png" width="288" alt="dma_buf_app"/></div><br/></div><p class="- topic/p p" data-ofbid="d313808e210__20250121171819">其中第 3 步的操作比较啰嗦fb 驱动根据 APP 传来的一组 fd[],逐个去向 DMA-BUF 模块申请以下资源dma_buf -&gt; attatch
-&gt; sg_table -&gt; dma_addr_t并将这组资源保存在本地struct aic_de_dmabuf用于释放
DMA-BUF。</p><p class="- topic/p p" data-ofbid="d313808e212__20250121171819">这里面有一个假设sg_table 中只有一个 sg。也就是说单个 DMA-BUF 必须是物理连续的,不能是多块 buf
拼起来的,否则物理地址 dma_addr_t 就不能代表多块 buf
的起始地址。</p><p class="- topic/p p" data-ofbid="d313808e215__20250121171819">对于释放过程APP 需要先通知 FB 驱动要释放的 fb[],然后再使用用户态文件接口 close()逐个关闭 fb[]。</p><div class="- topic/note note note note_note" id="display_preserve_memory_managment_process__note_pc3_q1g_d1c" data-ofbid="display_preserve_memory_managment_process__note_pc3_q1g_d1c"><span class="note__title">注:</span>
<p class="- topic/p p" data-ofbid="d313808e219__20250121171819">之所以申请“一组 fd[]”,是因为 video 播放过程中为了边显示边解码,至少需要两套 Buffer 来实现乒乓效果(实际应用中为了更流畅,可能还需要申请多套
Buffer 构成循环
Buffer。这里说的“一套 Buffer”对应视频的一帧数据而一帧视频数据往往分成 YUV 三个分量,每个分量在解码过程中是需要分别处理(对应 DE 寄存器中 addr0、addr1、addr2所以“一套 Buffer”应该是
Y、U、V 共 3 个 Buffer。对于乒乓结构的 Buffer就需要申请 3*2=6 个 BUF 的 fd。</p>
</div><p class="- topic/p p" data-ofbid="d313808e222__20250121171819">UI Layer 的 buffer 申请,是直接调用通用 DMA 接口 <span class="+ topic/keyword pr-d/apiname keyword apiname">dma_alloc_coherent()</span>,最终在
CMA 内存中分配。这样并不影响 Video Layer 的 buffer 走 DMA-BUF 申请CMA 模块内部会处理好来自各种接口的 Buffer
申请。</p></section></div></article></main></div>
</div>
<nav role="navigation" id="wh_topic_toc" aria-label="On this page" class="col-lg-2 d-none d-lg-block navbar d-print-none">
<div id="wh_topic_toc_content">
<div class=" wh_topic_toc "><div class="wh_topic_label">在本页上</div><ul><li class="section-item"><div class="section-title"><a href="#display_preserve_memory_managment_process__section_w5d_21g_d1c" data-tocid="display_preserve_memory_managment_process__section_w5d_21g_d1c">CMA</a></div></li><li class="section-item"><div class="section-title"><a href="#display_preserve_memory_managment_process__section_gvd_21g_d1c" data-tocid="display_preserve_memory_managment_process__section_gvd_21g_d1c">DMA-BUF</a></div></li></ul></div>
</div>
</nav>
</div>
</div>
</div>
<footer class="navbar navbar-default wh_footer">
<div class=" footer-container mx-auto ">
<title>footer def</title>
<style><!--
.p1 {
font-family: FangZhengShuSong, Times, serif;
}
.p2 {
font-family: Arial, Helvetica, sans-serif;
}
.p3 {
font-family: "Lucida Console", "Courier New", monospace;
}
--></style>
<div class="webhelp.fragment.footer">
<p class="p1">Copyright © 2019-2024 广东匠芯创科技有限公司. All rights reserved.</p>
</div><div>
<div class="generation_time">
Update Time: 2025-01-21
</div>
</div>
</div>
</footer>
<div id="go2top" class="d-print-none">
<span class="oxy-icon oxy-icon-up"></span>
</div>
<div id="modal_img_large" class="modal">
<span class="close oxy-icon oxy-icon-remove"></span>
<div id="modal_img_container"></div>
<div id="caption"></div>
</div>
<script src="${pd}/publishing/publishing-styles-AIC-template/js/custom.js" defer="defer"></script>
</body>
</html>