/* * linux-4.9/drivers/media/platform/sunxi-vfe/sample.c * * Copyright (c) 2007-2017 Allwinnertech Co., Ltd. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include "vfe.h" extern int vfe_open_special(int id); extern int vfe_close_special(int id); extern int vfe_s_input_special(int id, int sel); extern int vfe_s_fmt_special(int id, struct v4l2_format *f); extern int vfe_g_fmt_special(int id, struct v4l2_format *f); extern int vfe_dqbuffer_special(int id, struct vfe_buffer **buf); extern int vfe_qbuffer_special(int id, struct vfe_buffer *buf); extern int vfe_streamon_special(int id, enum v4l2_buf_type i); extern int vfe_streamoff_special(int id, enum v4l2_buf_type i); extern void vfe_register_buffer_done_callback(int id, void *func); extern int os_mem_alloc_sample(int id, struct vfe_mm *mem_man); extern void os_mem_free_sample(int id, struct vfe_mm *mem_man); #define BUF_NUM 8 #define ALIGN_4K(x) (((x) + (4095)) & ~(4095)) #define ALIGN_16B(x) (((x) + (15)) & ~(15)) int vfe_id; int width = 640; int height = 480; int frame_cnt; int save_cnt; struct work_struct save_work; struct vfe_mm ion_buf[BUF_NUM]; struct vfe_buffer video_buf[BUF_NUM]; char file_path_isp[100] = "/mnt/fb.yuv"; module_param(width, uint, S_IRUGO | S_IWUSR); module_param(height, uint, S_IRUGO | S_IWUSR); module_param(vfe_id, uint, S_IRUGO | S_IWUSR); module_param(save_cnt, uint, S_IRUGO | S_IWUSR); module_param_string(file_path_isp, file_path_isp, sizeof(file_path_isp), S_IRUGO | S_IWUSR); static int file_write(struct file *fp, char *buf, size_t len) { mm_segment_t old_fs; loff_t pos = 0; int buf_len; if (IS_ERR_OR_NULL(fp)) { pr_info("cfg write file error, fp is null!"); return -1; } old_fs = get_fs(); set_fs(KERNEL_DS); buf_len = vfs_write(fp, buf, len, &pos); set_fs(old_fs); if (buf_len < 0) return -1; if (buf_len != len) pr_info("buf_len = %x, len = %pa\n", buf_len, &len); return buf_len; } static void sample_save_frame(struct work_struct *work) { struct file *fp; int i, ret; struct vfe_buffer *vfe_buf; vfe_dqbuffer_special(vfe_id, &vfe_buf); fp = filp_open(file_path_isp, O_RDWR | O_APPEND | O_CREAT, 0666); if (IS_ERR_OR_NULL(fp)) { pr_info("open %s failed!, ERR NO is %ld.\n", file_path_isp, (long)fp); goto qbuf; } for (i = 0; i < BUF_NUM; ++i) { if (vfe_buf->paddr == ion_buf[i].dma_addr) break; } ret = file_write(fp, (char *)ion_buf[i].vir_addr, width*height*3/2); if (ret < 0) pr_info("file_write failed!, ERR NO is %d.\n", ret); ret = filp_close(fp, NULL); if (ret < 0) pr_info("file close failed!, ERR NO is %d.\n", ret); qbuf: vfe_qbuffer_special(vfe_id, vfe_buf); } static int sample_req_buffers(void) { unsigned int i; for (i = 0; i < BUF_NUM; ++i) { ion_buf[i].size = ALIGN_4K(ALIGN_16B(width)*height*3/2); if (os_mem_alloc_sample(vfe_id, &ion_buf[i]) < 0) { pr_info("alloc ion buffer failed\n"); return -1; } video_buf[i].paddr = ion_buf[i].dma_addr; if (vfe_qbuffer_special(vfe_id, &video_buf[i])) { pr_info("vfe_qbuffer_special failed\n"); return -1; } } return 0; } static int sample_free_buffers(void) { unsigned int i; struct vfe_buffer *vfe_buf; for (i = 0; i < BUF_NUM; ++i) { os_mem_free_sample(vfe_id, &ion_buf[i]); vfe_dqbuffer_special(vfe_id, &vfe_buf); } return 0; } static int sample_video_open(int sel) { if (vfe_open_special(vfe_id) < 0) { pr_info("vfe_open_special falied\n"); return -1; } if (vfe_s_input_special(vfe_id, 0) < 0) { pr_info("vfe_s_input_special %d error!\n", sel); return -1; } return 0; } static int sample_fmt_set(void) { struct v4l2_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; fmt.fmt.pix.field = V4L2_FIELD_NONE; if (vfe_s_fmt_special(vfe_id, &fmt) < 0) { pr_info("vfe_s_fmt_special error!\n"); return -1; } if (vfe_g_fmt_special(vfe_id, &fmt) < 0) { pr_info("vfe_g_fmt_special error!\n"); return -1; } else { pr_info("resolution got from sensor = %d*%d\n", fmt.fmt.pix.width, fmt.fmt.pix.height); } return 0; } void sample_buffer_process(int id) { struct vfe_buffer *vfe_buf; frame_cnt++; if (frame_cnt <= save_cnt) { pr_info("save frame %d\n", frame_cnt); schedule_work(&save_work); } else { vfe_dqbuffer_special(vfe_id, &vfe_buf); pr_info("csi %d buffer process\n", id); vfe_qbuffer_special(vfe_id, vfe_buf); } } int sample_start(void) { if (-1 == sample_video_open(0)) return -1; if (-1 == sample_fmt_set()) return -1; if (-1 == sample_req_buffers()) return -1; vfe_register_buffer_done_callback(vfe_id, sample_buffer_process); frame_cnt = 0; if (vfe_streamon_special(vfe_id, V4L2_BUF_TYPE_VIDEO_CAPTURE) < 0) { pr_info("vfe_streamon_special failed\n"); return -1; } else { pr_info("vfe_streamon_special ok\n"); } return 0; } int sample_end(void) { if (vfe_streamoff_special(vfe_id, V4L2_BUF_TYPE_VIDEO_CAPTURE) < 0) { pr_info("vfe_streamoff_special failed\n"); return -1; } else { pr_info("vfe_streamoff_special ok\n"); } if (-1 == sample_free_buffers()) return -1; if (vfe_close_special(vfe_id) < 0) { pr_info("vfe_close_special falied\n"); return -1; } return 0; } static int __init sample_init(void) { INIT_WORK(&save_work, sample_save_frame); return sample_start(); } static void __exit sample_exit(void) { flush_work(&save_work); sample_end(); } module_init(sample_init); module_exit(sample_exit); MODULE_AUTHOR("zhaowei"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("video kernel api test");