由于GPU 多媒体解码 camera输入以及overlay显示等操作需要大块的连续物理内存,S5PV210开发板在初始化的过程中,会为这些多媒体相关驱动预留内存,这些预留的物理内存不能再被系统的其他部件使用,因此调整这些预留空间使之既能满足项目的需求,同时把浪费部分最小化,有必要分析每一部分内存需求的计算公式。
FIMC0, FMIC1, FIMC2预留空间计算
在arch/arm/plat-s5p/bootmem.c中,s5p_reserve_bootmem为media设备预留内存
80 void s5p_reserve_bootmem(struct s5p_media_device *mdevs, int nr_mdevs)
81 {
82 struct s5p_media_device *mdev;
83 void *virt_mem;
84 int i;
85
86 media_devs = mdevs;
87 nr_media_devs = nr_mdevs;
88
89 for (i = 0; i < nr_media_devs; i++) {
90 mdev = &media_devs[i];
91 if (mdev->memsize <= 0)
92 continue;
93
94 if (mdev->paddr)
95 virt_mem = __alloc_bootmem(mdev->memsize, PAGE_SIZE,
96 mdev->paddr);
97 else {
98 printk(KERN_INFO "s5pv210: meminfo.back[mdev->bank].start=0x%08lx\n",
99 meminfo.bank[mdev->bank].start);
100 virt_mem = __alloc_bootmem(mdev->memsize, PAGE_SIZE,
101 meminfo.bank[mdev->bank].start);
102 }
103
104 if (virt_mem != NULL) {
105 mdev->paddr = virt_to_phys(virt_mem);
106 } else {
107 mdev->paddr = (dma_addr_t)NULL;
108 printk(KERN_INFO "s5p: Failed to reserve system memory\n");
109 }
110
111 printk(KERN_INFO "s5pv210: %lu bytes system memory reserved "
112 "for %s at 0x%08x\n", (unsigned long) mdev->memsize,
113 mdev->name, mdev->paddr);
114 }
115 }
@mdevs 是在mach-s5pv210.c中定义的
174 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (24576 * SZ_1K)
175 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * SZ_1K)
177 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (24576 * SZ_1K)
178 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (36864 * SZ_1K)
179 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (36864 * SZ_1K)
180 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD (S5PV210_LCD_WIDTH * \
181 S5PV210_LCD_HEIGHT * 4 * \
182 CONFIG_FB_S3C_NR_BUFFERS)
183 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG (8192 * SZ_1K)
184
185 /* 1920 * 1080 * 4 (RGBA)
186 * - framesize == 1080p : 1920 * 1080 * 2(16bpp) * 2(double buffer) = 8MB
187 * - framesize < 1080p : 1080 * 720 * 4(32bpp) * 2(double buffer) = under 8MB
188 **/
189 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_G2D (8192 * SZ_1K)
190 #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_TEXSTREAM (3000 * SZ_1K)
191 #define S5PV210_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 (3300 * SZ_1K)
192
193 static struct s5p_media_device smdkc110_media_devs[] = {
194 [0] = {
195 .id = S5P_MDEV_MFC,
196 .name = "mfc",
197 .bank = 0,
198 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0,
199 .paddr = 0,
200 },
201 [1] = {
202 .id = S5P_MDEV_MFC,
203 .name = "mfc",
204 .bank = 1,
205 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1,
206 .paddr = 0,
207 },
208 [2] = {
209 .id = S5P_MDEV_FIMC0,
210 .name = "fimc0",
211 .bank = 1,
212 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0,
213 .paddr = 0,
214 },
215 [3] = {
216 .id = S5P_MDEV_FIMC1,
217 .name = "fimc1",
218 .bank = 1,
219 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1,
220 .paddr = 0,
221 },
222 [4] = {
223 .id = S5P_MDEV_FIMC2,
224 .name = "fimc2",
225 .bank = 1,
226 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2,
227 .paddr = 0,
228 },
229 [5] = {
230 .id = S5P_MDEV_JPEG,
231 .name = "jpeg",
232 .bank = 0,
233 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG,
234 .paddr = 0,
235 },
236 [6] = {
237 .id = S5P_MDEV_FIMD,
238 .name = "fimd",
239 .bank = 1,
240 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD,
241 .paddr = 0,
242 },
243 [7] = {
244 .id = S5P_MDEV_TEXSTREAM,
245 .name = "texstream",
246 .bank = 1,
247 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_TEXSTREAM,
248 .paddr = 0,
249 },
250 [8] = {
251 .id = S5P_MDEV_PMEM_GPU1,
252 .name = "pmem_gpu1",
253 .bank = 0, /* OneDRAM */
254 .memsize = S5PV210_ANDROID_PMEM_MEMSIZE_PMEM_GPU1,
255 .paddr = 0,
256 },
257 [9] = {
258 .id = S5P_MDEV_G2D,
259 .name = "g2d",
260 .bank = 0,
261 .memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_G2D,
262 .paddr = 0,
263 },
264 };
结构smdkc110_media_devs定义了每个驱动的名字,物理内存的bank位置,需要的物理内存大小,以及希望的物理内存位置
下面我们一一分析如何计算每个驱动实际需要的物理内存大小
FIMC0,原生代码预留了32MB的物理内存空间
关于fimc0, fimc1, fimc2控制器的作用,参看http://blog.csdn.net/kickxxx/article/details/7728947
FIMC0在拍照以及preview时,系统从camera sensor获取数据时使用。拍照或者preview的数据需要放在request buffer中,应用通过VIDIOC_REQBUFS从内核申请request buffer,kernel从这块预留的物理内存分配buffer:
897 static int fimc_alloc_buffers(struct fimc_control *ctrl, int size[], int align)
898 {
899 struct fimc_capinfo *cap = ctrl->cap;
900 int i, plane;
901
902 for (i = 0; i < cap->nr_bufs; i++) {
903 for (plane = 0; plane < 4; plane++) {
904 cap->bufs[i].length[plane] = size[plane];
905 if (!cap->bufs[i].length[plane])
906 continue;
907
908 fimc_dma_alloc(ctrl, &cap->bufs[i], plane, align);
909
910 if (!cap->bufs[i].base[plane])
911 goto err_alloc;
912 }
913
914 cap->bufs[i].state = VIDEOBUF_PREPARED;
915 cap->bufs[i].id = i;
916 }
917
918 return 0;
919
920 err_alloc:
921 for (i = 0; i < cap->nr_bufs; i++) {
922 if (cap->bufs[i].base[plane])
923 fimc_dma_free(ctrl, &cap->bufs[i], plane);
924
925 memset(&cap->bufs[i], 0, sizeof(cap->bufs[i]));
926 }
927
928 return -ENOMEM;
929 }
预留空间计算公式 = cam->nr_bufs * SUM(size),因此CameraHal的申请的buffer队列越大,需要预留的空间越多,CameraHal中定义buffer count 为 8,此外kenerl本身也有一个最大限制FIMC_CAPBUFS(16)
@size之所以是个数组,是因为不同的pixelformat对应的层数不一样,size的大小和preview,capture的图片大小是成正比的,因此系统支持的camera sensor分辨率越高,@size越大
我们看下@size的计算方法
950 int fimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b)
..............
996 switch (cap->fmt.pixelformat) {
997 case V4L2_PIX_FMT_RGB32: /* fall through */
998 case V4L2_PIX_FMT_RGB565: /* fall through */
999 case V4L2_PIX_FMT_YUYV: /* fall through */
1000 case V4L2_PIX_FMT_UYVY: /* fall through */
1001 case V4L2_PIX_FMT_VYUY: /* fall through */
1002 case V4L2_PIX_FMT_YVYU: /* fall through */
1003 case V4L2_PIX_FMT_YUV422P: /* fall through */
1004 size[0] = cap->fmt.sizeimage;
1005 break;
1006
1007 case V4L2_PIX_FMT_NV16: /* fall through */
1008 case V4L2_PIX_FMT_NV61:
1009 size[0] = cap->fmt.width * cap->fmt.height;
1010 size[1] = cap->fmt.width * cap->fmt.height;
1011 size[3] = 16; /* Padding buffer */
1012 break;
1013 case V4L2_PIX_FMT_NV12:
1014 size[0] = cap->fmt.width * cap->fmt.height;
1015 size[1] = cap->fmt.width * cap->fmt.height/2;
1016 break;
1017 case V4L2_PIX_FMT_NV21:
1018 size[0] = cap->fmt.width * cap->fmt.height;
1019 size[1] = cap->fmt.width * cap->fmt.height/2;
1020 size[3] = 16; /* Padding buffer */
1021 break;
1022 case V4L2_PIX_FMT_NV12T:
1023 /* Tiled frame size calculations as per 4x2 tiles
1024 * - Width: Has to be aligned to 2 times the tile width
1025 * - Height: Has to be aligned to the tile height
1026 * - Alignment: Has to be aligned to the size of the
1027 * macrotile (size of 4 tiles)
1028 *
1029 * NOTE: In case of rotation, we need modified calculation as
1030 * width and height are aligned to different values.
1031 */
1032 if (cap->rotate == 90 || cap->rotate == 270) {
1033 size[0] = ALIGN(ALIGN(cap->fmt.height, 128) *
1034 ALIGN(cap->fmt.width, 32),
1035 SZ_8K);
1036 size[1] = ALIGN(ALIGN(cap->fmt.height, 128) *
1037 ALIGN(cap->fmt.width/2, 32),
1038 SZ_8K);
1039 } else {
1040 size[0] = ALIGN(ALIGN(cap->fmt.width, 128) *
1041 ALIGN(cap->fmt.height, 32),
1042 SZ_8K);
1043 size[1] = ALIGN(ALIGN(cap->fmt.width, 128) *
1044 ALIGN(cap->fmt.height/2, 32),
1045 SZ_8K);
1046 }
1047 align = SZ_8K;
1048 break;
1049
1050 case V4L2_PIX_FMT_YUV420:
1051 size[0] = cap->fmt.width * cap->fmt.height;
1052 size[1] = cap->fmt.width * cap->fmt.height >> 2;
1053 size[2] = cap->fmt.width * cap->fmt.height >> 2;
1054 size[3] = 16; /* Padding buffer */
1055 break;
1056
1057 case V4L2_PIX_FMT_JPEG:
1058 size[0] = fimc_camera_get_jpeg_memsize(ctrl);
1059 default:
1060 break;
1061 }
...............
fmt.width和fmt.height 和capture需要的图片尺寸大小或者preview的大小相关,因此需要根据项目预估这个值,我们的项目使用的是video AD转换芯片,sensor raw size最大为720*576,preview最大为WVGA 800*480,取二者的最大值720*576
一般来说上层应用使用YUYV或者NV12T作为image格式,考虑对齐方式和安全边界,那么每个buffer最大不会超过1024KB
因此我们可以大致估算FIMC0预留空间大小:
cam->nr_bufs * SUM(size) = 16 * 720 * 576 * 2 = 13MB
FIMC2
Recording过程中,中间件使用fimc0获取preview数据,使用fimc2获取recording的数据
预留空间计算公式 = buffer_count * (record_width * record_height * pixel_size)
CameraHal层在调用REQBUFS时指定buffer count大小为8,kernel对申请request buffer的数目限制为16
一般来说录像的宽高等于LCD的尺寸(当然这是项目相关的)
所以对于WVGA计算得到
cam->nr_bufs * (record_width * record_height * pixel_size) = 16 * (800 * 480 * 2) =13MB
FIMC1
FIMC1用来实现overlay功能,不论是camera preview还是视频播放,都会通过FIMC1进行数据转换,预留的内存空间用来保存转换后的数据,转换的image大小是和LCD屏尺寸相关
FIMC1预留空间计算公式=buffer_count * (lcd_width * lcd_height * pixel_size) + buffer_count *(video_width * video_height * pixel_size)
对于WVGA lcd, lcd_width=800, lcd_height=480, pixel_size根据pixelformat不同,分别为4, 2, 1.5,这里取最大值4
Video_width和video_height是解码后的视频宽高,有些解码器需要从overlay设备分配这个解码后端buffer,但有些解码器不需要从FIMC1申请内存。samsung平台的有些解码器在从FIMC1申请buffer失败后,会转到软件显示(surfaceflinger),申请后端解码buffer失败则转到软件解码。用例比较多,我自己也没搞太清楚。
buffer count虽然是CameraHAL申请request buffer 时设定的,但是kernel对最大buffer count做了限制 为FIMC_OUTBUFS=3
buffer_count * (lcd_width * lcd_height * pixel_size) = 3 * (800 * 480 * 4) = 4608000
buffer_count * (1080p * 3) = 3 * 1980 * 1300 * 4 = 29.45MB,注意这里的1300,我们一般认为1080P的高度为1080,实际上仍然有些不规范的视频高度超过了1300,三星在fimc_output.c对最大输入高度做了限制FIMC_SRC_MAX_H = 1300,所以在这里我们以1300为准
考虑对齐等因素,需要为FIMC1预留35MB