| /* |
| * drivers/amlogic/media/stream_input/parser/psparser.c |
| * |
| * Copyright (C) 2016 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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 <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/errno.h> |
| #include <linux/interrupt.h> |
| #include <linux/wait.h> |
| #include <linux/sched.h> |
| #include <linux/fs.h> |
| #include <linux/slab.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/amlogic/media/frame_sync/ptsserv.h> |
| #include <linux/amlogic/media/utils/amstream.h> |
| |
| #include <linux/uaccess.h> |
| /* #include <mach/am_regs.h> */ |
| #include <linux/amlogic/media/utils/vdec_reg.h> |
| #include "streambuf_reg.h" |
| #include "streambuf.h" |
| #include "psparser.h" |
| #include "../amports/amports_priv.h" |
| |
| |
| #define TIMESTAMP_IONLY 1 |
| #define SAVE_SCR 0 |
| |
| #define MPEG_START_CODE_PATTERN (0x00000100L) |
| #define MPEG_START_CODE_MASK (0xffffff00L) |
| #define MAX_MPG_AUDIOPK_SIZE 0x1000 |
| |
| #define SUB_INSERT_START_CODE_HIGH 0x414d4c55 |
| #define SUB_INSERT_START_CODE_LOW 0xaa000000 |
| |
| #define PARSER_WRITE (ES_WRITE | ES_PARSER_START) |
| #define PARSER_VIDEO (ES_TYPE_VIDEO) |
| #define PARSER_AUDIO (ES_TYPE_AUDIO) |
| #define PARSER_SUBPIC (ES_TYPE_SUBTITLE) |
| #define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START) |
| #define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START) |
| #define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START) |
| #define PARSER_BUSY (ES_PARSER_BUSY) |
| |
| #define PARSER_PARAMETER_LENGTH_BIT 16 |
| #define PARSER_PARAMETER_LOOP_BIT 24 |
| |
| #define PARSER_POP READ_PARSER_REG(PFIFO_DATA) |
| #define SET_BLOCK(size) \ |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, size, ES_PACK_SIZE_BIT, ES_PACK_SIZE_WID) |
| #define SET_DISCARD_SIZE(size) WRITE_PARSER_REG(PARSER_PARAMETER, size) |
| |
| #define VIDEO_AUTO_FLUSH |
| #ifdef VIDEO_AUTO_FLUSH |
| static u32 video_auto_flush_state; |
| #define VIDEO_AUTO_FLUSH_IDLE 0 |
| #define VIDEO_AUTO_FLUSH_MONITOR 1 |
| #define VIDEO_AUTO_FLUSH_TRIGGER 2 |
| #define VIDEO_AUTO_FLUSH_DONE 3 |
| #define VIDEO_AUTO_FLUSH_PTS_THRESHOLD 90000 |
| #define VIDEO_AUTO_FLUSH_BYTE_COUNT 1024 |
| |
| static s32 audio_last_pts; |
| static s32 audio_monitor_pts; |
| #endif |
| |
| enum { |
| SEARCH_START_CODE = 0, |
| SEND_VIDEO_SEARCH, |
| SEND_AUDIO_SEARCH, |
| SEND_SUBPIC_SEARCH, |
| DISCARD_SEARCH, |
| DISCARD_ONLY |
| #ifdef VIDEO_AUTO_FLUSH |
| , |
| SEARCH_START_CODE_VIDEO_FLUSH |
| #endif |
| }; |
| |
| enum { |
| AUDIO_FIRST_ACCESS_ARM = 0, |
| AUDIO_FIRST_ACCESS_POPING, |
| AUDIO_FIRST_ACCESS_DONE |
| }; |
| |
| static const char psparser_id[] = "psparser-id"; |
| |
| static DECLARE_WAIT_QUEUE_HEAD(wq); |
| |
| static struct tasklet_struct psparser_tasklet; |
| static u32 fetch_done; |
| static u8 audio_id, video_id, sub_id, sub_id_max; |
| static u32 audio_first_access; |
| static u32 packet_remaining; |
| static u32 video_data_parsed; |
| static u32 audio_data_parsed; |
| static u32 pts_equ_dts_flag; |
| |
| static unsigned int first_apts, first_vpts; |
| static unsigned int audio_got_first_pts, video_got_first_dts, sub_got_first_pts; |
| atomic_t sub_block_found = ATOMIC_INIT(0); |
| |
| #define DEBUG_VOB_SUB |
| #ifdef DEBUG_VOB_SUB |
| static u8 sub_found_num; |
| static struct subtitle_info *sub_info[MAX_SUB_NUM]; |
| #endif |
| |
| static bool ptsmgr_first_vpts_ready(void) |
| { |
| return (video_got_first_dts != 0) ? true : false; |
| } |
| |
| static bool ptsmgr_first_apts_ready(void) |
| { |
| return (audio_got_first_pts != 0) ? true : false; |
| } |
| |
| static void ptsmgr_vpts_checkin(u32 pts) |
| { |
| if (video_got_first_dts == 0) { |
| video_got_first_dts = 1; |
| first_vpts = pts; |
| } |
| |
| pts_checkin_offset(PTS_TYPE_VIDEO, video_data_parsed, pts); |
| } |
| |
| static void ptsmgr_apts_checkin(u32 pts) |
| { |
| if (audio_got_first_pts == 0) { |
| audio_got_first_pts = 1; |
| first_apts = pts; |
| } |
| /* apts_checkin(pts); */ |
| pts_checkin_offset(PTS_TYPE_AUDIO, audio_data_parsed, pts); |
| |
| #ifdef VIDEO_AUTO_FLUSH |
| audio_last_pts = pts; |
| |
| if ((video_auto_flush_state == VIDEO_AUTO_FLUSH_IDLE) |
| && ptsmgr_first_vpts_ready()) { |
| video_auto_flush_state = VIDEO_AUTO_FLUSH_MONITOR; |
| audio_monitor_pts = pts; |
| } |
| |
| if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR) { |
| if ((audio_last_pts - audio_monitor_pts) > |
| VIDEO_AUTO_FLUSH_PTS_THRESHOLD) |
| video_auto_flush_state = VIDEO_AUTO_FLUSH_TRIGGER; |
| } |
| #endif |
| } |
| |
| static u32 parser_process(s32 type, s32 packet_len) |
| { |
| s16 temp, header_len, misc_flags, i; |
| u32 pts = 0, dts = 0; |
| u32 pts_dts_flag = 0; |
| u16 invalid_pts = 0; |
| |
| temp = PARSER_POP; |
| packet_len--; |
| |
| if ((temp >> 6) == 0x02) { |
| /* mpeg-2 system */ |
| misc_flags = PARSER_POP; |
| header_len = PARSER_POP; |
| packet_len -= 2; |
| packet_len -= header_len; |
| |
| if ((misc_flags >> 6) > 1) { |
| /* PTS exist */ |
| pts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ |
| pts |= PARSER_POP << 22; /* bit 29-22 */ |
| pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ |
| pts |= (PARSER_POP << 7); /* bit 14-07 */ |
| pts |= (PARSER_POP >> 1); /* bit 06-00 */ |
| header_len -= 5; |
| pts_dts_flag |= 2; |
| } |
| |
| if ((misc_flags >> 6) > 2) { |
| /* DTS exist */ |
| dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ |
| dts |= PARSER_POP << 22; /* bit 29-22 */ |
| dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ |
| dts |= (PARSER_POP << 7); /* bit 14-07 */ |
| dts |= (PARSER_POP >> 1); /* bit 06-00 */ |
| header_len -= 5; |
| pts_dts_flag |= 1; |
| } |
| |
| if (misc_flags & 0x20) { |
| /* ESCR_flag */ |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| header_len -= 5; |
| } |
| |
| if (misc_flags & 0x10) { |
| /* ES_rate_flag */ |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| header_len -= 3; |
| } |
| |
| if (misc_flags & 0x08) { |
| /* DSM_trick_mode_flag */ |
| PARSER_POP; |
| header_len -= 1; |
| } |
| |
| if (misc_flags & 0x04) { |
| /* additional_copy_info_flag */ |
| PARSER_POP; |
| header_len -= 1; |
| } |
| |
| if (misc_flags & 0x02) { |
| /* PES_CRC_flag */ |
| PARSER_POP; |
| PARSER_POP; |
| header_len -= 2; |
| } |
| |
| if (misc_flags & 0x01) { |
| /* PES_extension_flag */ |
| misc_flags = PARSER_POP; |
| header_len--; |
| |
| if ((misc_flags & 0x80) && (header_len >= 128)) { |
| /* PES_private_data_flag */ |
| for (i = 0; i < 128; i++) |
| PARSER_POP; |
| |
| header_len -= 128; |
| } |
| #if 0 |
| if (misc_flags & 0x40) { |
| /* pack_header_field_flag */ |
| /* Invalid case */ |
| } |
| #endif |
| if (misc_flags & 0x20) { |
| /* program_packet_sequence_counter_flag */ |
| PARSER_POP; |
| PARSER_POP; |
| header_len -= 2; |
| } |
| |
| if (misc_flags & 0x10) { |
| /* PSTD_buffer_flag */ |
| PARSER_POP; |
| PARSER_POP; |
| header_len -= 2; |
| } |
| |
| if (misc_flags & 1) { |
| /* PES_extension_flag_2 */ |
| temp = PARSER_POP & 0x7f; |
| |
| while (temp) { |
| PARSER_POP; |
| temp--; |
| header_len--; |
| } |
| } |
| |
| while (header_len) { |
| PARSER_POP; |
| header_len--; |
| } |
| } |
| |
| while (header_len) { |
| PARSER_POP; |
| header_len--; |
| } |
| |
| } else { |
| /* mpeg-1 system */ |
| while (temp == 0xff) { |
| temp = PARSER_POP; |
| packet_len--; |
| } |
| |
| if ((temp >> 6) == 1) { |
| PARSER_POP; /* STD buffer size */ |
| temp = PARSER_POP; |
| packet_len -= 2; |
| } |
| |
| if (((temp >> 4) == 2) || ((temp >> 4) == 3)) { |
| pts = ((temp >> 1) & 7) << 30; /* bit 32-30 */ |
| pts |= PARSER_POP << 22; /* bit 29-22 */ |
| pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ |
| pts |= (PARSER_POP << 7); /* bit 14-07 */ |
| pts |= (PARSER_POP >> 1); /* bit 06-00 */ |
| packet_len -= 4; |
| pts_dts_flag |= 2; |
| } |
| |
| if ((temp >> 4) == 3) { |
| dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ |
| dts |= PARSER_POP << 22; /* bit 29-22 */ |
| dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ |
| dts |= (PARSER_POP << 7); /* bit 14-07 */ |
| dts |= (PARSER_POP >> 1); /* bit 06-00 */ |
| packet_len -= 5; |
| pts_dts_flag |= 1; |
| } |
| } |
| |
| if ((pts == 0) && (dts == 0xffffffff)) { |
| invalid_pts = 1; |
| pr_info("invalid pts\n"); |
| } |
| |
| if (!packet_len) |
| return SEARCH_START_CODE; |
| |
| else if (type == 0) { |
| #ifdef VIDEO_AUTO_FLUSH |
| if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR) |
| audio_monitor_pts = audio_last_pts; |
| #endif |
| |
| if ((pts_dts_flag) && (!invalid_pts)) { |
| #if TIMESTAMP_IONLY |
| if (!ptsmgr_first_vpts_ready()) { |
| if (pts_dts_flag & 2) |
| ptsmgr_vpts_checkin(pts); |
| else |
| ptsmgr_vpts_checkin(dts); |
| } else if ((pts_dts_flag & 3) == 3) { |
| if (pts_equ_dts_flag) { |
| if (dts == pts) |
| ptsmgr_vpts_checkin(pts); |
| } else { |
| if (dts == pts) |
| pts_equ_dts_flag = 1; |
| ptsmgr_vpts_checkin(pts); |
| } |
| } |
| #else |
| if (!ptsmgr_first_vpts_ready()) { |
| if (pts_dts_flag & 2) |
| ptsmgr_vpts_checkin(pts); |
| else |
| ptsmgr_vpts_checkin(dts); |
| } else if (pts_dts_flag & 2) |
| ptsmgr_vpts_checkin(pts); |
| #endif |
| } |
| |
| if (ptsmgr_first_vpts_ready() || invalid_pts) { |
| SET_BLOCK(packet_len); |
| video_data_parsed += packet_len; |
| return SEND_VIDEO_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| |
| } else if (type == 1) { |
| /* mpeg audio */ |
| if (pts_dts_flag & 2) |
| ptsmgr_apts_checkin(pts); |
| |
| if (ptsmgr_first_apts_ready()) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| |
| } else if (type == 2) { |
| /* Private stream */ |
| temp = PARSER_POP; /* sub_stream_id */ |
| packet_len--; |
| |
| if (((temp & 0xf8) == 0xa0) && (temp == audio_id)) { |
| /* DVD_VIDEO Audio LPCM data */ |
| PARSER_POP; |
| temp = (PARSER_POP << 8) | PARSER_POP; |
| if (temp == 0) |
| temp = 4; |
| temp--; |
| packet_len -= 3; |
| |
| if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) { |
| if (temp) { |
| packet_remaining = packet_len - temp; |
| SET_DISCARD_SIZE(temp); |
| audio_first_access = |
| AUDIO_FIRST_ACCESS_POPING; |
| return DISCARD_ONLY; |
| } |
| |
| audio_first_access = AUDIO_FIRST_ACCESS_DONE; |
| |
| if (packet_len) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else |
| return SEARCH_START_CODE; |
| |
| } else { |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| packet_len -= 3; |
| } |
| |
| if (pts_dts_flag & 2) |
| ptsmgr_apts_checkin(pts); |
| |
| if (ptsmgr_first_apts_ready()) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| |
| } else if (((temp & 0xf8) == 0x80) && (temp == audio_id)) { |
| /* Audio AC3 data */ |
| PARSER_POP; |
| temp = (PARSER_POP << 8) | PARSER_POP; |
| packet_len -= 3; |
| |
| if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) { |
| if (pts_dts_flag & 2) |
| ptsmgr_apts_checkin(pts); |
| |
| if ((temp > 2) && (packet_len > (temp - 2))) { |
| temp -= 2; |
| packet_remaining = packet_len - temp; |
| SET_DISCARD_SIZE(temp); |
| audio_first_access = |
| AUDIO_FIRST_ACCESS_POPING; |
| return DISCARD_ONLY; |
| } |
| |
| audio_first_access = AUDIO_FIRST_ACCESS_DONE; |
| |
| if (packet_len) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else |
| return SEARCH_START_CODE; |
| } |
| |
| if (pts_dts_flag & 2) |
| ptsmgr_apts_checkin(pts); |
| |
| if (ptsmgr_first_apts_ready()) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| |
| } else if (((temp & 0xf8) == 0x88) && (temp == audio_id)) { |
| /* Audio DTS data */ |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| packet_len -= 3; |
| |
| if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) |
| audio_first_access = AUDIO_FIRST_ACCESS_DONE; |
| |
| if (pts_dts_flag & 2) |
| ptsmgr_apts_checkin(pts); |
| |
| if (ptsmgr_first_apts_ready()) { |
| SET_BLOCK(packet_len); |
| audio_data_parsed += packet_len; |
| return SEND_AUDIO_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| } else if ((temp & 0xe0) == 0x20) { |
| if (temp > sub_id_max) |
| sub_id_max = temp; |
| #ifdef DEBUG_VOB_SUB |
| for (i = 0; i < sub_found_num; i++) { |
| if (!sub_info[i]) |
| break; |
| if (temp == sub_info[i]->id) |
| break; |
| } |
| if (i == sub_found_num && i < MAX_SUB_NUM) { |
| if (sub_info[sub_found_num]) { |
| sub_info[sub_found_num]->id = temp; |
| sub_found_num++; |
| pr_info |
| ("[%s]found new sub_id=0x%x (num %d)\n", |
| __func__, temp, sub_found_num); |
| } else { |
| pr_info |
| ("[%s]sub info NULL!\n", __func__); |
| } |
| } |
| #endif |
| |
| if (temp == sub_id) { |
| /* DVD sub-picture data */ |
| if (!packet_len) |
| return SEARCH_START_CODE; |
| |
| else { |
| #if 0 |
| if (pts_dts_flag & 2) |
| ptsmgr_spts_checkin(pts); |
| |
| if (ptsmgr_first_spts_ready()) { |
| SET_BLOCK(packet_len); |
| return SEND_SUBPIC_SEARCH; |
| |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| #else |
| if (pts_dts_flag & 2) |
| sub_got_first_pts = 1; |
| |
| if (sub_got_first_pts) { |
| pr_info |
| ("sub pts 0x%x, len %d\n", |
| pts, packet_len); |
| SET_BLOCK(packet_len); |
| WRITE_PARSER_REG |
| (PARSER_PARAMETER, |
| 16 << |
| PARSER_PARAMETER_LENGTH_BIT); |
| WRITE_PARSER_REG |
| (PARSER_INSERT_DATA, |
| SUB_INSERT_START_CODE_HIGH); |
| WRITE_PARSER_REG |
| (PARSER_INSERT_DATA, |
| SUB_INSERT_START_CODE_LOW | |
| get_sub_type()); |
| WRITE_PARSER_REG |
| (PARSER_INSERT_DATA, |
| packet_len); |
| WRITE_PARSER_REG |
| (PARSER_INSERT_DATA, pts); |
| atomic_set(&sub_block_found, 1); |
| return SEND_SUBPIC_SEARCH; |
| } |
| |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| #endif |
| } |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| } else { |
| SET_DISCARD_SIZE(packet_len); |
| return DISCARD_SEARCH; |
| } |
| } |
| |
| return SEARCH_START_CODE; |
| } |
| |
| static void on_start_code_found(int start_code) |
| { |
| unsigned short packet_len; |
| unsigned short temp; |
| unsigned int next_action; |
| #if SAVE_SCR |
| unsigned int scr; |
| #endif |
| |
| if (atomic_read(&sub_block_found)) { |
| wakeup_sub_poll(); |
| atomic_set(&sub_block_found, 0); |
| } |
| |
| if (audio_first_access == AUDIO_FIRST_ACCESS_POPING) { |
| /* |
| *we are in the procedure of poping data for audio first |
| * access, continue with last packet |
| */ |
| audio_first_access = AUDIO_FIRST_ACCESS_DONE; |
| |
| if (packet_remaining) { |
| next_action = SEND_AUDIO_SEARCH; |
| SET_BLOCK(packet_remaining); |
| |
| } else |
| next_action = SEARCH_START_CODE; |
| |
| } else if (start_code == 0xba) { /* PACK_START_CODE */ |
| temp = PARSER_POP; |
| |
| if ((temp >> 6) == 0x01) { |
| #if SAVE_SCR |
| scr = ((temp >> 3) & 0x3) << 30; /* bit 31-30 */ |
| scr |= (temp & 0x3) << 28; /* bit 29-28 */ |
| scr |= (PARSER_POP) << 20; /* bit 27-20 */ |
| temp = PARSER_POP; |
| scr |= (temp >> 4) << 16; /* bit 19-16 */ |
| scr |= (temp & 7) << 13; /* bit 15-13 */ |
| scr |= (PARSER_POP) << 5; /* bit 12-05 */ |
| scr |= (PARSER_POP) >> 3; /* bit 04-00 */ |
| #else |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| #endif |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| temp = PARSER_POP & 7; |
| |
| while (temp) { /* stuff byte */ |
| PARSER_POP; |
| temp--; |
| } |
| |
| } else { |
| /* mpeg-1 Pack Header */ |
| #if SAVE_SCR |
| scr = ((temp >> 1) & 0x3) << 30; /* bit 31-30 */ |
| scr |= (PARSER_POP) << 22; /* bit 29-22 */ |
| scr |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ |
| scr |= (PARSER_POP) << 7; /* bit 14-07 */ |
| scr |= (PARSER_POP >> 1); /* bit 06-00 */ |
| #else |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| PARSER_POP; |
| #endif |
| } |
| |
| #ifdef VIDEO_AUTO_FLUSH |
| if (video_auto_flush_state == VIDEO_AUTO_FLUSH_TRIGGER) { |
| next_action = SEARCH_START_CODE_VIDEO_FLUSH; |
| video_auto_flush_state = VIDEO_AUTO_FLUSH_DONE; |
| } else |
| #endif |
| |
| next_action = SEARCH_START_CODE; |
| |
| } else { |
| packet_len = (PARSER_POP << 8) | PARSER_POP; |
| |
| if (start_code == video_id) |
| next_action = parser_process(0, packet_len); |
| |
| else if (start_code == audio_id) { |
| /* add mpeg audio packet length check */ |
| if (packet_len > MAX_MPG_AUDIOPK_SIZE) |
| next_action = SEARCH_START_CODE; |
| |
| else |
| next_action = parser_process(1, packet_len); |
| |
| } else if (start_code == 0xbb) { |
| SET_DISCARD_SIZE(packet_len); |
| next_action = DISCARD_SEARCH; |
| } else if (start_code == 0xbd) |
| next_action = parser_process(2, packet_len); |
| |
| else if (start_code == 0xbf) { |
| SET_DISCARD_SIZE(packet_len); |
| next_action = DISCARD_SEARCH; |
| } else if ((start_code < 0xc0) || (start_code > 0xc8)) |
| next_action = SEARCH_START_CODE; |
| |
| else if (packet_len) { |
| SET_DISCARD_SIZE(packet_len); |
| next_action = DISCARD_SEARCH; |
| |
| } else |
| next_action = SEARCH_START_CODE; |
| } |
| |
| switch (next_action) { |
| case SEARCH_START_CODE: |
| WRITE_PARSER_REG(PARSER_CONTROL, PARSER_AUTOSEARCH); |
| break; |
| |
| case SEND_VIDEO_SEARCH: |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| PARSER_AUTOSEARCH | PARSER_VIDEO | |
| PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID); |
| break; |
| |
| case SEND_AUDIO_SEARCH: |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| PARSER_AUTOSEARCH | PARSER_AUDIO | |
| PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID); |
| break; |
| |
| case SEND_SUBPIC_SEARCH: |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| PARSER_AUTOSEARCH | PARSER_SUBPIC | |
| PARSER_WRITE | ES_INSERT_BEFORE_ES_WRITE, |
| ES_CTRL_BIT, ES_CTRL_WID); |
| break; |
| |
| case DISCARD_SEARCH: |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| PARSER_AUTOSEARCH | PARSER_DISCARD, |
| ES_CTRL_BIT, ES_CTRL_WID); |
| break; |
| |
| case DISCARD_ONLY: |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| PARSER_DISCARD, ES_CTRL_BIT, ES_CTRL_WID); |
| break; |
| |
| #ifdef VIDEO_AUTO_FLUSH |
| case SEARCH_START_CODE_VIDEO_FLUSH: |
| WRITE_PARSER_REG(PARSER_INSERT_DATA, 0xffffffff); |
| WRITE_PARSER_REG(PARSER_INSERT_DATA, 0xffffffff); |
| WRITE_PARSER_REG(PARSER_PARAMETER, |
| ((VIDEO_AUTO_FLUSH_BYTE_COUNT / |
| 8) << PARSER_PARAMETER_LOOP_BIT) | (8 << |
| PARSER_PARAMETER_LENGTH_BIT)); |
| WRITE_PARSER_REG(PARSER_CONTROL, |
| PARSER_AUTOSEARCH | PARSER_VIDEO | PARSER_WRITE | |
| ES_INSERT_BEFORE_ES_WRITE); |
| break; |
| #endif |
| } |
| } |
| |
| static void parser_tasklet(ulong data) |
| { |
| s32 sc; |
| u32 int_status = READ_PARSER_REG(PARSER_INT_STATUS); |
| |
| WRITE_PARSER_REG(PARSER_INT_STATUS, int_status); |
| |
| if (int_status & PARSER_INTSTAT_FETCH_CMD) { |
| fetch_done = 1; |
| |
| wake_up_interruptible(&wq); |
| } |
| |
| if (int_status & PARSER_INTSTAT_SC_FOUND) { |
| sc = PARSER_POP; |
| |
| on_start_code_found(sc); |
| |
| } else if (int_status & PARSER_INTSTAT_DISCARD) |
| on_start_code_found(0); |
| } |
| |
| static irqreturn_t parser_isr(int irq, void *dev_id) |
| { |
| tasklet_schedule(&psparser_tasklet); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static ssize_t _psparser_write(const char __user *buf, size_t count) |
| { |
| size_t r = count; |
| const char __user *p = buf; |
| u32 len; |
| int ret; |
| dma_addr_t dma_addr = 0; |
| |
| if (r > 0) { |
| len = min_t(size_t, r, FETCHBUF_SIZE); |
| if (copy_from_user(fetchbuf, p, len)) |
| return -EFAULT; |
| |
| dma_addr = |
| dma_map_single(amports_get_dma_device(), |
| fetchbuf, FETCHBUF_SIZE, DMA_TO_DEVICE); |
| if (dma_mapping_error(amports_get_dma_device(), dma_addr)) |
| return -EFAULT; |
| |
| |
| fetch_done = 0; |
| |
| wmb(); /* Ensure fetchbuf contents visible */ |
| |
| WRITE_PARSER_REG(PARSER_FETCH_ADDR, dma_addr); |
| |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len); |
| dma_unmap_single(amports_get_dma_device(), dma_addr, |
| FETCHBUF_SIZE, DMA_TO_DEVICE); |
| ret = |
| wait_event_interruptible_timeout(wq, fetch_done != 0, |
| HZ / 10); |
| if (ret == 0) { |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, 0); |
| pr_info("write timeout, retry\n"); |
| return -EAGAIN; |
| } else if (ret < 0) |
| return -ERESTARTSYS; |
| |
| p += len; |
| r -= len; |
| } |
| |
| return count - r; |
| } |
| |
| s32 psparser_init(u32 vid, u32 aid, u32 sid, struct vdec_s *vdec) |
| { |
| s32 r; |
| u32 parser_sub_start_ptr; |
| u32 parser_sub_end_ptr; |
| u32 parser_sub_rp; |
| |
| #ifdef DEBUG_VOB_SUB |
| u8 i; |
| |
| for (i = 0; i < MAX_SUB_NUM; i++) { |
| sub_info[i] = kzalloc(sizeof(struct subtitle_info), GFP_KERNEL); |
| if (!sub_info[i]) { |
| pr_info |
| ("[psparser_init]alloc for subtitle info failed\n"); |
| } else |
| sub_info[i]->id = -1; |
| } |
| sub_found_num = 0; |
| #endif |
| parser_sub_start_ptr = READ_PARSER_REG(PARSER_SUB_START_PTR); |
| parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR); |
| parser_sub_rp = READ_PARSER_REG(PARSER_SUB_RP); |
| |
| video_id = vid; |
| audio_id = aid; |
| sub_id = sid; |
| audio_got_first_pts = 0; |
| video_got_first_dts = 0; |
| sub_got_first_pts = 0; |
| first_apts = 0; |
| first_vpts = 0; |
| pts_equ_dts_flag = 0; |
| |
| #ifdef VIDEO_AUTO_FLUSH |
| video_auto_flush_state = VIDEO_AUTO_FLUSH_IDLE; |
| #endif |
| |
| pr_info("video 0x%x, audio 0x%x, sub 0x%x\n", video_id, audio_id, |
| sub_id); |
| if (fetchbuf == 0) { |
| pr_info("%s: no fetchbuf\n", __func__); |
| return -ENOMEM; |
| } |
| |
| WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER); |
| |
| /* TS data path */ |
| #ifndef CONFIG_AM_DVB |
| WRITE_DEMUX_REG(FEC_INPUT_CONTROL, 0); |
| #else |
| tsdemux_set_reset_flag(); |
| #endif |
| CLEAR_DEMUX_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE); |
| CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE); |
| CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE); |
| CLEAR_DEMUX_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE)); |
| |
| /* hook stream buffer with PARSER */ |
| WRITE_PARSER_REG(PARSER_VIDEO_START_PTR, vdec->input.start); |
| WRITE_PARSER_REG(PARSER_VIDEO_END_PTR, |
| vdec->input.start + vdec->input.size - 8); |
| |
| if (vdec_single(vdec)) { |
| CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); |
| WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| } else { |
| SET_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); |
| WRITE_PARSER_REG(PARSER_VIDEO_WP, vdec->input.start); |
| WRITE_PARSER_REG(PARSER_VIDEO_RP, vdec->input.start); |
| } |
| |
| WRITE_PARSER_REG(PARSER_AUDIO_START_PTR, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| WRITE_PARSER_REG(PARSER_AUDIO_END_PTR, |
| READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR)); |
| CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); |
| |
| WRITE_PARSER_REG(PARSER_CONFIG, |
| (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | |
| (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | |
| (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); |
| |
| if (vdec_single(vdec)) { |
| WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| } |
| |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| |
| WRITE_PARSER_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_start_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_WP, parser_sub_start_ptr); |
| SET_PARSER_REG_MASK(PARSER_ES_CONTROL, |
| (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); |
| |
| WRITE_PARSER_REG(PFIFO_RD_PTR, 0); |
| WRITE_PARSER_REG(PFIFO_WR_PTR, 0); |
| |
| WRITE_PARSER_REG(PARSER_SEARCH_PATTERN, MPEG_START_CODE_PATTERN); |
| WRITE_PARSER_REG(PARSER_SEARCH_MASK, MPEG_START_CODE_MASK); |
| |
| WRITE_PARSER_REG(PARSER_CONFIG, |
| (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | |
| (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | |
| PS_CFG_STARTCODE_WID_24 | |
| PS_CFG_PFIFO_ACCESS_WID_8 | /* single byte pop */ |
| (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); |
| WRITE_PARSER_REG(PARSER_CONTROL, PARSER_AUTOSEARCH); |
| |
| tasklet_init(&psparser_tasklet, parser_tasklet, 0); |
| r = pts_start(PTS_TYPE_VIDEO); |
| if (r < 0) |
| goto Err_1; |
| r = pts_start(PTS_TYPE_AUDIO); |
| if (r < 0) |
| goto Err_2; |
| |
| video_data_parsed = 0; |
| audio_data_parsed = 0; |
| /*TODO irq */ |
| |
| r = vdec_request_irq(PARSER_IRQ, parser_isr, |
| "psparser", (void *)psparser_id); |
| |
| if (r) { |
| pr_info("PS Demux irq register failed.\n"); |
| |
| r = -ENOENT; |
| goto Err_3; |
| } |
| |
| WRITE_PARSER_REG(PARSER_INT_STATUS, 0xffff); |
| WRITE_PARSER_REG(PARSER_INT_ENABLE, |
| PARSER_INT_ALL << PARSER_INT_HOST_EN_BIT); |
| |
| return 0; |
| |
| Err_3: |
| pts_stop(PTS_TYPE_AUDIO); |
| |
| Err_2: |
| pts_stop(PTS_TYPE_VIDEO); |
| |
| Err_1: |
| return r; |
| } |
| |
| void psparser_release(void) |
| { |
| u8 i; |
| |
| pr_info("psparser_release\n"); |
| |
| WRITE_PARSER_REG(PARSER_INT_ENABLE, 0); |
| /*TODO irq */ |
| |
| vdec_free_irq(PARSER_IRQ, (void *)psparser_id); |
| |
| pts_stop(PTS_TYPE_VIDEO); |
| pts_stop(PTS_TYPE_AUDIO); |
| #ifdef DEBUG_VOB_SUB |
| for (i = 0; i < MAX_SUB_NUM; i++) |
| kfree(sub_info[i]); |
| pr_info("psparser release subtitle info\n"); |
| #endif |
| } |
| EXPORT_SYMBOL(psparser_release); |
| |
| ssize_t psparser_write(struct file *file, |
| struct stream_buf_s *vbuf, |
| struct stream_buf_s *abuf, |
| const char __user *buf, size_t count) |
| { |
| s32 r; |
| |
| struct port_priv_s *priv = (struct port_priv_s *)file->private_data; |
| struct stream_port_s *port = priv->port; |
| |
| if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) { |
| if (file->f_flags & O_NONBLOCK) |
| return -EAGAIN; |
| |
| if ((port->flag & PORT_FLAG_VID) |
| && (stbuf_space(vbuf) < count)) { |
| r = stbuf_wait_space(vbuf, count); |
| if (r < 0) |
| return r; |
| } |
| if ((port->flag & PORT_FLAG_AID) |
| && (stbuf_space(abuf) < count)) { |
| r = stbuf_wait_space(abuf, count); |
| if (r < 0) |
| return r; |
| } |
| } |
| |
| return _psparser_write(buf, count); |
| } |
| |
| void psparser_change_avid(unsigned int vid, unsigned int aid) |
| { |
| video_id = vid; |
| audio_id = aid; |
| } |
| |
| void psparser_change_sid(unsigned int sid) |
| { |
| sub_id = sid; |
| } |
| |
| void psparser_audio_reset(void) |
| { |
| ulong flags; |
| |
| DEFINE_SPINLOCK(lock); |
| |
| spin_lock_irqsave(&lock, flags); |
| |
| WRITE_PARSER_REG(PARSER_AUDIO_WP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| WRITE_PARSER_REG(PARSER_AUDIO_RP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| |
| WRITE_PARSER_REG(PARSER_AUDIO_START_PTR, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| WRITE_PARSER_REG(PARSER_AUDIO_END_PTR, |
| READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR)); |
| CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); |
| |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| |
| audio_data_parsed = 0; |
| |
| spin_unlock_irqrestore(&lock, flags); |
| |
| } |
| |
| void psparser_sub_reset(void) |
| { |
| ulong flags; |
| |
| DEFINE_SPINLOCK(lock); |
| u32 parser_sub_start_ptr; |
| u32 parser_sub_end_ptr; |
| |
| spin_lock_irqsave(&lock, flags); |
| |
| parser_sub_start_ptr = READ_PARSER_REG(PARSER_SUB_START_PTR); |
| parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR); |
| |
| WRITE_PARSER_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_start_ptr); |
| WRITE_PARSER_REG(PARSER_SUB_WP, parser_sub_start_ptr); |
| SET_PARSER_REG_MASK(PARSER_ES_CONTROL, |
| (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); |
| |
| spin_unlock_irqrestore(&lock, flags); |
| |
| } |
| |
| u8 psparser_get_sub_found_num(void) |
| { |
| #ifdef DEBUG_VOB_SUB |
| return sub_found_num; |
| #else |
| return 0; |
| #endif |
| } |
| |
| u8 psparser_get_sub_info(struct subtitle_info **sub_infos) |
| { |
| #ifdef DEBUG_VOB_SUB |
| u8 i = 0; |
| int ret = 0; |
| u8 size = sizeof(struct subtitle_info); |
| |
| for (i = 0; i < sub_found_num; i++) { |
| if (!sub_info[i]) { |
| pr_info |
| ("[psparser_get_sub_info:%d] sub_info[%d] NULL\n", |
| __LINE__, i); |
| ret = -1; |
| break; |
| } |
| if (!sub_infos[i]) { |
| pr_info |
| ("[psparser_get_sub_info:%d] sub_infos[%d] NULL\n", |
| __LINE__, i); |
| ret = -2; |
| break; |
| } |
| memcpy(sub_infos[i], sub_info[i], size); |
| } |
| return ret; |
| #else |
| return 0; |
| #endif |
| } |