郁悶的,用MFC放出來的H.264視屏竟然是黑白的,無奈跟蹤下MFC的調(diào)用流程:
內(nèi)核啟動初始化:
1)執(zhí)行s3c_mfc_init(),打印“S3C6400 MFC Driver, (c) 2007 Samsung Electronics”;
2)注冊平臺設(shè)備platform_driver_register(&s3c_mfc_driver);
3)進入s3c_mfc_probe();
4)獲取時鐘、打開時鐘,映射寄存器物理地址,獲取中斷、注冊中斷;
5)調(diào)用s3c_get_media_memory(S3C_MDEV_MFC),獲取顯存空間;
6)調(diào)用s3c_mfc_setup_memory(),設(shè)置Bitprocessor buffer, Data buffer;
7)調(diào)用s3c_mfc_memmap_databuf,打印以下信息“s3c_mfc_memmap_databuf: virtual address of data buffer = 0xc0700000”;
8)調(diào)用s3c_mfc_init_yuvbuf_mgr(),This function initializes the MfcFramBufMgr(Buffer Segment Manager);
最后打印“address of segment_info=c0bd6400, address of commit_info =c0be0040”
9)調(diào)用s3c_mfc_init_hw(),初始化硬件;
1. Reset the MFC IP
2. Download Firmware code into MFC
輸出:“s3c_mfc_init_hw: downloading firmware into bitprocessor”;
3. Start Bit Processor
4. Set the Base Address Registers for the following 3 buffers(CODE_BUF, WORKING_BUF, PARAMETER_BUF)
5. Set the Control Registers
輸出:
s3c_mfc_get_firmware_ver: GET_FW_VER command was issued
s3c_mfc_get_firmware_ver: GET_FW_VER => 0xf202, 0x130e
s3c_mfc_get_firmware_ver: BUSY_FLAG => 0
10)最后再次輸出banner:S3C6400 MFC Driver, (c) 2007 Samsung Electronics。
H.264 decode時:
0)上層調(diào)用s3c_mfc_open();
1)調(diào)用s3c_mfc_init_yuvbuf_mgr();
2)調(diào)用s3c_mfc_yuv_buffer_mgr_final(),表示不需要重新分配yuv buffer;
3)調(diào)用s3c_mfc_init_hw(),重新初始化硬件;
4)調(diào)用s3c_mfc_inst_create創(chuàng)建一個實例->s3c_mfc_get_stream_buffer_addr()獲取流地址;
輸出:
s3c_mfc_get_stream_buffer_addr: ctx->stream_buffer address 0xc0700000
s3c_mfc_get_stream_buffer_addr: ctx->phys_addr_stream_buffer address 0x50700000
s3c_mfc_inst_create: state = 10 /* instance is created but not initialized */
5)輸出s3c_mfc_open: mfc open success,open結(jié)束。
6)調(diào)用s3c_mfc_ioctl,發(fā)送命令: cmd = 80000f,S3C_MFC_IOCTL_MFC_GET_LINE_BUF_ADDR
copy_from_user
處理XXXXXX
copy_to_user
7)調(diào)用s3c_mfc_ioctl,發(fā)送命令: cmd = 800005,S3C_MFC_IOCTL_MFC_H264_DEC_INIT
copy_from_user
codec_mode = AVC_DEC;
Initialize MFC Instance
——>s3c_mfc_inst_init_dec
(1)checking state
(2)codec_mode
(3)stream size checking
(4)判斷instance序列號
(5)s3c_mfc_set_eos(0);
(6)s3c_mfc_issue_command();
(7)s3c_mfc_stream_end();
(8)獲取視屏參數(shù)
(9)s3c_mfc_get_yuv_buffer_addr——>s3c_mfc_print_commit_yuv_buffer_info();
(10)s3c_mfc_get_param_buff_virt_addr();
/*
* set the parameters in the parameters buffer for SET_FRAME_BUF command.
* buffer address of y, cb, cr will be set in parameters buffer before issuing SET_FRAME_BUF command
*/
(11)s3c_mfc_issue_command(ctx->inst_no, ctx->codec_mode, SET_FRAME_BUF);
(12)changing state to 20;
輸出信息:
s3c_mfc_inst_init_dec: strm_leng = 7767
s3c_mfc_inst_init_dec: ctx->inst_no = 0
s3c_mfc_inst_init_dec: ctx->codec_mode = 2
s3c_mfc_inst_init_dec: sequece bit buffer size = 500 (kb)
s3c_mfc_inst_init_dec: RET_DEC_SEQ_SRC_SIZE = 737680
s3c_mfc_inst_init_dec: RET_DEC_SEQ_SRC_FRAME_RATE = 0
s3c_mfc_inst_init_dec: RET_DEC_SEQ_FRAME_NEED_COUNT = 3
s3c_mfc_inst_init_dec: RET_DEC_SEQ_FRAME_DELAY = 0
s3c_mfc_inst_init_dec: width = 720, height = 400, buf_width = 720, buf_height = 400
s3c_mfc_print_commit_yuv_buffer_info: commit index = 000, base segment index = 0
s3c_mfc_print_commit_yuv_buffer_info: commit index = 000, number of segment = 1325
s3c_mfc_get_yuv_buffer_addr: ctx->inst_no : 0
s3c_mfc_get_yuv_buffer_addr: ctx->yuv_buffer : 0xc07fa000
s3c_mfc_get_yuv_buffer_addr: ctx->phys_addr_yuv_buffer : 0x507fa000
回到ioctrl
將初始化信息copy_to_user
8)視屏播放過程中,反復(fù)調(diào)用
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800011
9)cmd = 800007,S3C_MFC_IOCTL_MFC_H264_DEC_EXE
copy_from_user從用戶態(tài)獲取數(shù)據(jù),sizeof(s3c_mfc_enc_exe_arg_t)
s3c_mfc_inst_dec 對數(shù)據(jù)進行decode,真正decode函數(shù),此函數(shù)需要重點關(guān)注
copy_to_user將數(shù)據(jù)返回用戶態(tài)
難道直接將YUV數(shù)據(jù)返回用戶?然后用戶態(tài)再操作Framebuffer???
10)cmd = 800011,S3C_MFC_IOCTL_MFC_GET_YUV_BUF_ADDR
copy_from_user,sizeof(s3c_mfc_enc_exe_arg_t)
yuv_size = (pMfcInst->buf_width * pMfcInst->buf_height * 3) >> 1;
args.get_buf_addr.out_buf_size = yuv_size;
in_usr_data = (unsigned int)args.get_buf_addr.in_usr_data;
yuv_buffer = (unsigned int)pMfcInst->yuv_buffer;
run_index = pMfcInst->run_index;
out_buf_size = args.get_buf_addr.out_buf_size;
databuf_vaddr = (unsigned int)s3c_mfc_get_databuf_virt_addr();
offset = yuv_buffer + run_index * out_buf_size - databuf_vaddr;
copy_to_user
11)解碼完畢,調(diào)用s3c_mfc_release釋放instance
s3c_mfc_release: deleting instance number = 0
僅僅分析了調(diào)用過程,下一步需要研究解碼過程這兩個操作究竟是干什么的,有什么區(qū)別
cmd = 800007,S3C_MFC_IOCTL_MFC_H264_DEC_EXE
cmd = 800011,S3C_MFC_IOCTL_MFC_GET_YUV_BUF_ADDR
阿虛(Rockie Cheng)
上次寫到吃中午飯,匆匆收筆,后來調(diào)wifi,竟然忘了。
趁周末,把這個坑填上。
cmd = 800007,S3C_MFC_IOCTL_MFC_H264_DEC_EXE
s3c_mfc_inst_dec 對數(shù)據(jù)進行decode
s3c_mfc_inst_dec函數(shù)分析
簡介: this function decodes the input stream and put the decoded frame into the yuv buffer
輸入?yún)?shù)
s3c_mfc_inst_context_t *ctx 結(jié)構(gòu)體包含instance number,stream buffer和yuv buffer
unsigned long strm_leng stream長度
1) checking state(忽略所有涉及到旋轉(zhuǎn)的代碼)
2)大部分情況下,根據(jù)inst_no選擇對應(yīng)的MFC物理端口(8個)
3)計算size:frm_size = ctx->buf_width * ctx->buf_height;
4)
/* DEC_PIC_OPTION was newly added for MP4ASP */
writel(0x7, s3c_mfc_sfr_base_virt_addr + S3C_MFC_PARAM_DEC_PIC_OPTION);
writel(ctx->mv_mbyte_addr, s3c_mfc_sfr_base_virt_addr + S3C_MFC_PARAM_DEC_PIC_MV_ADDR);
writel(ctx->mv_mbyte_addr + 25920, s3c_mfc_sfr_base_virt_addr + S3C_MFC_PARAM_DEC_PIC_MBTYPE_ADDR);
這段代碼中的寄存器我的數(shù)據(jù)手冊里面竟然沒有,看來應(yīng)該更新了
5)將stream物理地址寫入CMD_DEC_PIC_BB_START (0x1AC)
需要4byte對齊
4-byte aligned byte address of the decoder input picture stream buffer
6)不對齊的數(shù)據(jù)寫入CMD_DEC_PIC_START_BYTE (0x1B0)
Byte Address of valid bitstream in input picture stream buffer
7)將stream length寫入CMD_DEC_PIC_CHUNK_SIZE (0x1A8)
Byte size of picture stream data
8)s3c_mfc_issue_command(ctx->inst_no, ctx->codec_mode, PIC_RUN)
啟動解碼函數(shù)
根據(jù)S3C_MFC_IOCTL_MFC_H264_DEC_INIT的配置,codec_mode應(yīng)該為2
將inst_no寫入RunIndex (0x168)
將codec_mode = 2寫入RunCodStd (0x16C)
將run命令寫入RunCommand (0x164),啟動解碼
掛起interruptible_sleep_on_timeout(&s3c_mfc_wait_queue, 500)
9)讀取命令執(zhí)行結(jié)果狀態(tài)寄存器RET_DEC_PIC_SUCCESS (0x1D8)
10)讀取RET_DEC_PIC_IDX (0x1C4)
Display frame index
After BIT decodes one frame, BIT return display frame index to this register.
并判斷幀情況,是否結(jié)尾,是否失敗等等
11)轉(zhuǎn)換inst_no的狀態(tài)
//* changing state
//* state change to S3C_MFC_INST_STATE_DEC_PIC_RUN_LINE_BUF
S3C_MFC_INST_STATE_TRANSITION(ctx, S3C_MFC_INST_STATE_DEC_PIC_RUN_LINE_BUF);
12)返回OK
===========================================================================
IOCTRL:cmd = 800011,S3C_MFC_IOCTL_MFC_GET_YUV_BUF_ADDR
DEC部分分析,這一段并沒有涉及到解碼,解碼主要在上面完成
out = copy_from_user(&args.get_buf_addr,
(s3c_mfc_get_buf_addr_arg_t *)arg,
sizeof(s3c_mfc_get_buf_addr_arg_t));
if (pMfcInst->yuv_buffer == NULL) {
mfc_err("mfc frame buffer is not internally allocated yet\n");
mutex_unlock(s3c_mfc_mutex);
return -EFAULT;
}
/* FRAM_BUF address is calculated differently for Encoder and Decoder. */
switch (pMfcInst->codec_mode) {
case MP4_DEC:
case AVC_DEC:
case VC1_DEC:
case H263_DEC:
/* Decoder case */
yuv_size = (pMfcInst->buf_width * pMfcInst->buf_height * 3) >> 1; //計算YUV的大小
args.get_buf_addr.out_buf_size = yuv_size;//填充out_buf_size尺寸
in_usr_data = (unsigned int)args.get_buf_addr.in_usr_data;
yuv_buffer = (unsigned int)pMfcInst->yuv_buffer;
run_index = pMfcInst->run_index;
out_buf_size = args.get_buf_addr.out_buf_size;
databuf_vaddr = (unsigned int)s3c_mfc_get_databuf_virt_addr();
offset = yuv_buffer + run_index * out_buf_size - databuf_vaddr;//計算偏移量
args.get_buf_addr.out_buf_addr = in_usr_data + offset;//填充out_buf地址
break;
} /* end of switch (codec_mode) */
args.get_buf_addr.ret_code = S3C_MFC_INST_RET_OK;
out = copy_to_user((s3c_mfc_get_buf_addr_arg_t *)arg, &args.get_buf_addr, sizeof(s3c_mfc_get_buf_addr_arg_t));
break;
阿虛(Rockie Cheng)
今天用三星的測試程序跑了一下H.264,發(fā)現(xiàn)顏色正常,打印出的信息有所不同。
covia android解碼時的ioctrl命令為
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800011
測試程序解碼為
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800013
前面分析過s3c_mfc_ioctl: cmd = 800011是將YUV數(shù)據(jù)返回用戶
而s3c_mfc_ioctl: cmd = 800013和前者不一樣的地方在于:
1)databuf_paddr = (unsigned int)S3C_MFC_BASEADDR_DATA_BUF;
//直接用寄存器(Physical Base Address for the MFC Data Buffer )地址填充
2)args.get_buf_addr.out_buf_addr = databuf_paddr + offset;
//輸出地址用paddr填充,databuf_paddr
分析:
MFC轉(zhuǎn)換出的數(shù)據(jù)格式是YUV的。
三星的測試程序使用了Post Processor驅(qū)動,800013命令返回物理地址給pp使用,直接刷到fb上。
推測covia的驅(qū)動可能沒有使用pp,而是手動做的YUV2RGB再刷到fb,僅僅是推測,因為q5的內(nèi)核也含有pp驅(qū)動。
附錄:一些代碼及調(diào)試信息
case S3C_MFC_IOCTL_MFC_GET_PHY_FRAM_BUF_ADDR:
mutex_lock(s3c_mfc_mutex);
out = copy_from_user(&args.get_buf_addr,
(s3c_mfc_get_buf_addr_arg_t *)arg,
sizeof(s3c_mfc_get_buf_addr_arg_t));
yuv_size = (pMfcInst->buf_width * pMfcInst->buf_height * 3) >> 1;
args.get_buf_addr.out_buf_size = yuv_size;
yuv_buffer = (unsigned int)pMfcInst->yuv_buffer;
run_index = pMfcInst->run_index;
out_buf_size = args.get_buf_addr.out_buf_size;
databuf_vaddr = (unsigned int)s3c_mfc_get_databuf_virt_addr();
databuf_paddr = (unsigned int)S3C_MFC_BASEADDR_DATA_BUF;
offset = yuv_buffer + run_index * out_buf_size - databuf_vaddr;
args.get_buf_addr.out_buf_addr = databuf_paddr + offset;
args.get_buf_addr.ret_code = S3C_MFC_INST_RET_OK;
out = copy_to_user((s3c_mfc_get_buf_addr_arg_t *)arg,
&args.get_buf_addr, sizeof(s3c_mfc_get_buf_addr_arg_t));
mutex_unlock(s3c_mfc_mutex);
break;
========= S3C6400/6410 Demo Application ==========
= =
= 1. H.264 display =
= 2. MPEG4 display =
= 3. H.263 display =
= 4. VC-1 display =
= 5. 4-windows display =
= 6. Display using local path =
= 7. Display using double buffering =
= 8. Camera preview & MFC encoding =
= 9. MFC decoding & Camera preview =
= 10. Camera preview & MFC encoding/decoding =
= 11. Camera input and JPEG encoding =
= 12. JPEG decoding and display =
= 13. Exit =
= =
==================================================
Select number --> 1
s3c_mfc_yuv_buffer_mgr_final
address of segment_info=c141c400, address of commit_info =c1426040
s3c_mfc_init_hw: downloading firmware into bitprocessor
s3c_mfc_get_firmware_ver: GET_FW_VER command was issued
s3c_mfc_get_firmware_ver: GET_FW_VER => 0xf202, 0x130e
s3c_mfc_get_firmware_ver: BUSY_FLAG => 0
s3c_mfc_get_stream_buffer_addr: ctx->stream_buffer address 0xc0f46000
s3c_mfc_get_stream_buffer_addr: ctx->phys_addr_stream_buffer address 0x50f46000
s3c_mfc_inst_create: state = 10
s3c_mfc_open: mfc open success
s3c_mfc_ioctl: cmd = 80000f
s3c_mfc_ioctl: cmd = 800005
s3c_mfc_inst_init_dec: strm_leng = 7956
s3c_mfc_inst_init_dec: ctx->inst_no = 0
s3c_mfc_inst_init_dec: ctx->codec_mode = 2
s3c_mfc_inst_init_dec: sequece bit buffer size = 500 (kb)
s3c_mfc_inst_init_dec: RET_DEC_SEQ_SRC_SIZE = 327920
s3c_mfc_inst_init_dec: RET_DEC_SEQ_SRC_FRAME_RATE = 0
s3c_mfc_inst_init_dec: RET_DEC_SEQ_FRAME_NEED_COUNT = 4
s3c_mfc_inst_init_dec: RET_DEC_SEQ_FRAME_DELAY = 0
s3c_mfc_inst_init_dec: width = 320, height = 240, buf_width = 320, buf_height = 240
s3c_mfc_print_commit_yuv_buffer_info: commit index = 000, base segment index = 0
s3c_mfc_print_commit_yuv_buffer_info: commit index = 000, number of segment = 509
s3c_mfc_get_yuv_buffer_addr: ctx->inst_no : 0
s3c_mfc_get_yuv_buffer_addr: ctx->yuv_buffer : 0xc1040000
s3c_mfc_get_yuv_buffer_addr: ctx->phys_addr_yuv_buffer : 0x51040000
s3c_mfc_ioctl: cmd = 800007
########<STREAMINFO> width=320 height=240.
s3c_mfc_ioctl: cmd = 800013
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800013
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800013
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800013
s3c_mfc_ioctl: cmd = 800007
s3c_mfc_ioctl: cmd = 800013