| /* |
| * drivers/amlogic/media/stream_input/parser/esparser.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/mutex.h> |
| #include <linux/slab.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/amlogic/media/frame_sync/ptsserv.h> |
| |
| #include <linux/uaccess.h> |
| #include <linux/atomic.h> |
| |
| /* #include <mach/am_regs.h> */ |
| #include <linux/delay.h> |
| |
| #include "../../frame_provider/decoder/utils/vdec.h" |
| #include <linux/amlogic/media/utils/vdec_reg.h> |
| #include "../amports/streambuf_reg.h" |
| #include "../amports/streambuf.h" |
| #include "esparser.h" |
| #include "../amports/amports_priv.h" |
| #include "../amports/thread_rw.h" |
| |
| #include <linux/amlogic/media/codec_mm/codec_mm.h> |
| |
| |
| |
| #define SAVE_SCR 0 |
| |
| #define ES_START_CODE_PATTERN 0x00000100 |
| #define ES_START_CODE_MASK 0xffffff00 |
| #define SEARCH_PATTERN_LEN 512 |
| #define ES_PARSER_POP READ_PARSER_REG(PFIFO_DATA) |
| |
| #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 MAX_DRM_PACKAGE_SIZE 0x500000 |
| |
| |
| static unsigned char *search_pattern; |
| static dma_addr_t search_pattern_map; |
| static u32 audio_real_wp; |
| static u32 audio_buf_start; |
| static u32 audio_buf_end; |
| |
| static const char esparser_id[] = "esparser-id"; |
| |
| static DECLARE_WAIT_QUEUE_HEAD(wq); |
| |
| |
| static u32 search_done; |
| static u32 video_data_parsed; |
| static u32 audio_data_parsed; |
| static atomic_t esparser_use_count = ATOMIC_INIT(0); |
| static DEFINE_MUTEX(esparser_mutex); |
| |
| static inline u32 get_buf_wp(u32 type) |
| { |
| if (type == BUF_TYPE_AUDIO) |
| return audio_real_wp; |
| else |
| return 0; |
| } |
| static inline u32 get_buf_start(u32 type) |
| { |
| if (type == BUF_TYPE_AUDIO) |
| return audio_buf_start; |
| else |
| return 0; |
| } |
| static inline u32 get_buf_end(u32 type) |
| { |
| if (type == BUF_TYPE_AUDIO) |
| return audio_buf_end; |
| else |
| return 0; |
| } |
| static void set_buf_wp(u32 type, u32 wp) |
| { |
| if (type == BUF_TYPE_AUDIO) { |
| audio_real_wp = wp; |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP, wp/* & 0xffffff00*/); |
| } |
| return; |
| } |
| |
| static irqreturn_t esparser_isr(int irq, void *dev_id) |
| { |
| u32 int_status = READ_PARSER_REG(PARSER_INT_STATUS); |
| |
| WRITE_PARSER_REG(PARSER_INT_STATUS, int_status); |
| |
| if (int_status & PARSER_INTSTAT_SC_FOUND) { |
| WRITE_PARSER_REG(PFIFO_RD_PTR, 0); |
| WRITE_PARSER_REG(PFIFO_WR_PTR, 0); |
| search_done = 1; |
| wake_up_interruptible(&wq); |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static inline u32 buf_wp(u32 type) |
| { |
| u32 wp; |
| |
| if ((READ_PARSER_REG(PARSER_ES_CONTROL) & ES_VID_MAN_RD_PTR) == 0) { |
| wp = |
| #if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ |
| (type == BUF_TYPE_HEVC) ? READ_VREG(HEVC_STREAM_WR_PTR) : |
| #endif |
| (type == BUF_TYPE_VIDEO) ? READ_VREG(VLD_MEM_VIFIFO_WP) : |
| (type == BUF_TYPE_AUDIO) ? |
| READ_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) : |
| READ_PARSER_REG(PARSER_SUB_START_PTR); |
| } else { |
| wp = |
| #if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ |
| (type == BUF_TYPE_HEVC) ? READ_PARSER_REG(PARSER_VIDEO_WP) : |
| #endif |
| (type == BUF_TYPE_VIDEO) ? READ_PARSER_REG(PARSER_VIDEO_WP) : |
| (type == BUF_TYPE_AUDIO) ? |
| READ_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) : |
| READ_PARSER_REG(PARSER_SUB_START_PTR); |
| } |
| |
| return wp; |
| } |
| |
| static int esparser_stbuf_write(struct stream_buf_s *stbuf, const u8 *buf, u32 count) |
| { |
| size_t r = count; |
| const char __user *p = buf; |
| |
| u32 len = 0; |
| u32 parser_type; |
| int ret; |
| u32 wp; |
| dma_addr_t dma_addr = 0; |
| u32 type = stbuf->type; |
| |
| VDEC_PRINT_FUN_LINENO(__func__, __LINE__); |
| if (type == BUF_TYPE_HEVC) |
| parser_type = PARSER_VIDEO; |
| else if (type == BUF_TYPE_VIDEO) |
| parser_type = PARSER_VIDEO; |
| else if (type == BUF_TYPE_AUDIO) |
| parser_type = PARSER_AUDIO; |
| else |
| parser_type = PARSER_SUBPIC; |
| |
| wp = buf_wp(type); |
| |
| if (r > 0) { |
| if (stbuf->is_phybuf) |
| len = count; |
| else { |
| len = min_t(size_t, r, (size_t) 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_t) dma_addr)) |
| return -EFAULT; |
| |
| } |
| |
| /* wmb(); don't need */ |
| /* reset the Write and read pointer to zero again */ |
| WRITE_PARSER_REG(PFIFO_RD_PTR, 0); |
| WRITE_PARSER_REG(PFIFO_WR_PTR, 0); |
| |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, len, ES_PACK_SIZE_BIT, |
| ES_PACK_SIZE_WID); |
| WRITE_PARSER_REG_BITS(PARSER_CONTROL, |
| parser_type | PARSER_WRITE | |
| PARSER_AUTOSEARCH, ES_CTRL_BIT, |
| ES_CTRL_WID); |
| |
| if (stbuf->is_phybuf) { |
| u32 buf_32 = (unsigned long)buf & 0xffffffff; |
| WRITE_PARSER_REG(PARSER_FETCH_ADDR, buf_32); |
| } else { |
| WRITE_PARSER_REG(PARSER_FETCH_ADDR, dma_addr); |
| dma_unmap_single(amports_get_dma_device(), dma_addr, |
| FETCHBUF_SIZE, DMA_TO_DEVICE); |
| } |
| |
| search_done = 0; |
| if (!(stbuf->drm_flag & TYPE_PATTERN)) { |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, |
| (7 << FETCH_ENDIAN) | len); |
| WRITE_PARSER_REG(PARSER_FETCH_ADDR, search_pattern_map); |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, |
| (7 << FETCH_ENDIAN) | SEARCH_PATTERN_LEN); |
| } else { |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, |
| (7 << FETCH_ENDIAN) | (len + 512)); |
| } |
| |
| ret = wait_event_interruptible_timeout(wq, search_done != 0, |
| HZ / 5); |
| if (ret == 0) { |
| WRITE_PARSER_REG(PARSER_FETCH_CMD, 0); |
| |
| if (wp == buf_wp(type)) { |
| /*no data fetched */ |
| return -EAGAIN; |
| } else { |
| pr_info("W Timeout, but fetch ok,"); |
| pr_info("type %d len=%d,wpdiff=%d, isphy %x\n", |
| type, len, wp - buf_wp(type), stbuf->is_phybuf); |
| } |
| } else if (ret < 0) |
| return -ERESTARTSYS; |
| } |
| |
| if ((type == BUF_TYPE_VIDEO) |
| || (has_hevc_vdec() && (type == BUF_TYPE_HEVC))) |
| video_data_parsed += len; |
| else if (type == BUF_TYPE_AUDIO) |
| audio_data_parsed += len; |
| |
| threadrw_update_buffer_level(stbuf, len); |
| VDEC_PRINT_FUN_LINENO(__func__, __LINE__); |
| |
| return len; |
| } |
| |
| static ssize_t _esparser_write(const char __user *buf, |
| size_t count, struct stream_buf_s *stbuf, int isphybuf) |
| { |
| return esparser_stbuf_write(stbuf, buf, count); |
| } |
| |
| static ssize_t _esparser_write_s(const char __user *buf, |
| size_t count, struct stream_buf_s *stbuf) |
| { |
| size_t r = count; |
| const char __user *p = buf; |
| u32 len = 0; |
| int ret; |
| u32 wp, buf_start, buf_end; |
| u32 type = stbuf->type; |
| void *vaddr = NULL; |
| |
| if (type != BUF_TYPE_AUDIO) |
| BUG(); |
| wp = get_buf_wp(type); |
| buf_end = get_buf_end(type) + 8; |
| buf_start = get_buf_start(type); |
| /*pr_info("write wp 0x%x, count %d, start 0x%x, end 0x%x\n", |
| * wp, (u32)count, buf_start, buf_end);*/ |
| if (wp + count > buf_end) { |
| if (wp == buf_end) { |
| wp = buf_start; |
| set_buf_wp(type, wp); |
| return -EAGAIN; |
| } |
| vaddr = codec_mm_phys_to_virt(wp); |
| ret = copy_from_user(vaddr, p, buf_end - wp); |
| if (ret > 0) { |
| len += buf_end - wp - ret; |
| codec_mm_dma_flush(vaddr, len, DMA_TO_DEVICE); |
| wp += len; |
| pr_info("copy from user not finished\n"); |
| set_buf_wp(type, wp); |
| goto end_write; |
| } else if (ret == 0) { |
| len += buf_end - wp; |
| codec_mm_dma_flush(vaddr, len, DMA_TO_DEVICE); |
| wp = buf_start; |
| r = count - len; |
| set_buf_wp(type, wp); |
| } else { |
| pr_info("copy from user failed 1\n"); |
| pr_info("w wp 0x%x, count %d, start 0x%x end 0x%x\n", |
| wp, (u32)count, buf_start, buf_end); |
| return -EAGAIN; |
| } |
| } |
| |
| vaddr = codec_mm_phys_to_virt(wp); |
| ret = copy_from_user(vaddr, p + len, r); |
| if (ret >= 0) { |
| len += r - ret; |
| codec_mm_dma_flush(vaddr, r - ret, DMA_TO_DEVICE); |
| if (ret > 0) |
| pr_info("copy from user not finished 2\n"); |
| wp += r - ret; |
| set_buf_wp(type, wp); |
| } else { |
| pr_info("copy from user failed 2\n"); |
| return -EAGAIN; |
| } |
| |
| end_write: |
| if (type == BUF_TYPE_AUDIO) |
| { |
| audio_data_parsed += len; |
| threadrw_update_buffer_level(stbuf, len); |
| } |
| |
| return len; |
| } |
| |
| s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64) |
| { |
| u32 passed; |
| |
| if (buf->write_thread) |
| passed = threadrw_dataoffset(buf); |
| else |
| passed = video_data_parsed; |
| return pts_checkin_offset_us64(PTS_TYPE_VIDEO, passed, us64); |
| |
| } |
| |
| s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64) |
| { |
| u32 passed; |
| |
| if (buf->write_thread) |
| passed = threadrw_dataoffset(buf); |
| else |
| passed = audio_data_parsed; |
| return pts_checkin_offset_us64(PTS_TYPE_AUDIO, passed, us64); |
| } |
| |
| s32 es_vpts_checkin(struct stream_buf_s *buf, u32 pts) |
| { |
| #if 0 |
| if (buf->first_tstamp == INVALID_PTS) { |
| buf->flag |= BUF_FLAG_FIRST_TSTAMP; |
| buf->first_tstamp = pts; |
| return 0; |
| } |
| #endif |
| u32 passed = 0; |
| |
| mutex_lock(&esparser_mutex); |
| passed = video_data_parsed + threadrw_buffer_level(buf); |
| mutex_unlock(&esparser_mutex); |
| |
| return pts_checkin_offset(PTS_TYPE_VIDEO, passed, pts); |
| |
| } |
| |
| s32 es_apts_checkin(struct stream_buf_s *buf, u32 pts) |
| { |
| #if 0 |
| if (buf->first_tstamp == INVALID_PTS) { |
| buf->flag |= BUF_FLAG_FIRST_TSTAMP; |
| buf->first_tstamp = pts; |
| |
| return 0; |
| } |
| #endif |
| u32 passed = 0; |
| mutex_lock(&esparser_mutex); |
| passed = audio_data_parsed + threadrw_buffer_level(buf); |
| mutex_unlock(&esparser_mutex); |
| |
| return pts_checkin_offset(PTS_TYPE_AUDIO, passed, pts); |
| } |
| |
| s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec) |
| { |
| s32 r = 0; |
| u32 pts_type; |
| u32 parser_sub_start_ptr; |
| u32 parser_sub_end_ptr; |
| u32 parser_sub_rp; |
| bool first_use = false; |
| /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ |
| VDEC_PRINT_FUN_LINENO(__func__, __LINE__); |
| |
| if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC)) |
| pts_type = PTS_TYPE_HEVC; |
| else |
| /* #endif */ |
| if (buf->type == BUF_TYPE_VIDEO) |
| pts_type = PTS_TYPE_VIDEO; |
| else if (buf->type == BUF_TYPE_AUDIO) |
| pts_type = PTS_TYPE_AUDIO; |
| else if (buf->type == BUF_TYPE_SUBTITLE) |
| pts_type = PTS_TYPE_MAX; |
| else |
| return -EINVAL; |
| mutex_lock(&esparser_mutex); |
| 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); |
| |
| buf->flag |= BUF_FLAG_PARSER; |
| |
| if (atomic_add_return(1, &esparser_use_count) == 1) { |
| first_use = true; |
| |
| if (fetchbuf == 0) { |
| pr_info("%s: no fetchbuf\n", __func__); |
| r = -ENOMEM; |
| goto Err_1; |
| } |
| |
| if (search_pattern == NULL) { |
| search_pattern = kcalloc(1, |
| SEARCH_PATTERN_LEN, |
| GFP_KERNEL); |
| |
| if (search_pattern == NULL) { |
| pr_err("%s: no search_pattern\n", __func__); |
| r = -ENOMEM; |
| goto Err_1; |
| } |
| |
| /* build a fake start code to get parser interrupt */ |
| search_pattern[0] = 0x00; |
| search_pattern[1] = 0x00; |
| search_pattern[2] = 0x01; |
| search_pattern[3] = 0xff; |
| |
| search_pattern_map = dma_map_single( |
| amports_get_dma_device(), |
| search_pattern, |
| SEARCH_PATTERN_LEN, |
| DMA_TO_DEVICE); |
| } |
| |
| /* reset PARSER with first esparser_init() call */ |
| WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER); |
| /* for recorded file and local play, this can't change the input source*/ |
| /* 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)); |
| |
| 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)); |
| |
| WRITE_PARSER_REG(PFIFO_RD_PTR, 0); |
| WRITE_PARSER_REG(PFIFO_WR_PTR, 0); |
| |
| WRITE_PARSER_REG(PARSER_SEARCH_PATTERN, ES_START_CODE_PATTERN); |
| WRITE_PARSER_REG(PARSER_SEARCH_MASK, ES_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); |
| |
| } |
| |
| /* hook stream buffer with PARSER */ |
| if (has_hevc_vdec() && (pts_type == PTS_TYPE_HEVC)) { |
| 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); |
| |
| /* set vififo_vbuf_rp_sel=>hevc */ |
| WRITE_VREG(DOS_GEN_CTRL0, 3 << 1); |
| |
| /* set use_parser_vbuf_wp */ |
| SET_VREG_MASK(HEVC_STREAM_CONTROL, |
| (1 << 3) | (0 << 4)); |
| /* set stream_fetch_enable */ |
| SET_VREG_MASK(HEVC_STREAM_CONTROL, 1); |
| |
| if (buf->no_parser) { |
| /*set endian for non-parser mode */ |
| SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4); |
| } |
| |
| /* set stream_buffer_hole with 256 bytes */ |
| SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1 << 29)); |
| } 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); |
| } |
| video_data_parsed = 0; |
| } else if (pts_type == PTS_TYPE_VIDEO) { |
| 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) || (vdec_get_debug_flags() & 0x2)) { |
| if (vdec_get_debug_flags() & 0x2) |
| pr_info("%s %d\n", __func__, __LINE__); |
| 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); |
| |
| if (has_hevc_vdec()) { |
| /* set vififo_vbuf_rp_sel=>vdec */ |
| WRITE_VREG(DOS_GEN_CTRL0, 0); |
| } |
| } 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); |
| } |
| video_data_parsed = 0; |
| } else if (pts_type == PTS_TYPE_AUDIO) { |
| /* set wp as buffer start */ |
| SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, |
| MEM_BUFCTRL_MANUAL); |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3); |
| SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, |
| MEM_BUFCTRL_INIT); |
| CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, |
| MEM_BUFCTRL_INIT); |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| audio_data_parsed = 0; |
| audio_buf_start = |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR); |
| audio_real_wp = audio_buf_start; |
| audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR); |
| } else if (buf->type == BUF_TYPE_SUBTITLE) { |
| 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_rp); |
| SET_PARSER_REG_MASK(PARSER_ES_CONTROL, |
| (7 << ES_SUB_WR_ENDIAN_BIT) | |
| ES_SUB_MAN_RD_PTR); |
| } |
| |
| if (pts_type < PTS_TYPE_MAX) { |
| r = pts_start(pts_type); |
| |
| if (r < 0) { |
| pr_info("esparser_init: pts_start failed\n"); |
| goto Err_1; |
| } |
| } |
| #if 0 |
| if (buf->flag & BUF_FLAG_FIRST_TSTAMP) { |
| if (buf->type == BUF_TYPE_VIDEO) |
| es_vpts_checkin(buf, buf->first_tstamp); |
| else if (buf->type == BUF_TYPE_AUDIO) |
| es_apts_checkin(buf, buf->first_tstamp); |
| |
| buf->flag &= ~BUF_FLAG_FIRST_TSTAMP; |
| } |
| #endif |
| |
| if (first_use) { |
| /*TODO irq */ |
| r = vdec_request_irq(PARSER_IRQ, esparser_isr, |
| "parser", (void *)esparser_id); |
| |
| if (r) { |
| pr_info("esparser_init: irq register failed.\n"); |
| goto Err_2; |
| } |
| VDEC_PRINT_FUN_LINENO(__func__, __LINE__); |
| |
| WRITE_PARSER_REG(PARSER_INT_STATUS, 0xffff); |
| WRITE_PARSER_REG(PARSER_INT_ENABLE, |
| PARSER_INTSTAT_SC_FOUND << |
| PARSER_INT_HOST_EN_BIT); |
| } |
| mutex_unlock(&esparser_mutex); |
| |
| if (!(vdec_get_debug_flags() & 1) && |
| !codec_mm_video_tvp_enabled()) { |
| int block_size = (buf->type == BUF_TYPE_AUDIO) ? |
| PAGE_SIZE : PAGE_SIZE << 4; |
| int buf_num = (buf->type == BUF_TYPE_AUDIO) ? |
| 20 : (2 * SZ_1M)/(PAGE_SIZE << 4); |
| if (!(buf->type == BUF_TYPE_SUBTITLE)) |
| buf->write_thread = threadrw_alloc(buf_num, |
| block_size, |
| esparser_write_ex, |
| (buf->type == BUF_TYPE_AUDIO) ? 1 : 0); |
| /*manul mode for audio*/ |
| } |
| |
| return 0; |
| |
| Err_2: |
| pts_stop(pts_type); |
| |
| Err_1: |
| atomic_dec(&esparser_use_count); |
| buf->flag &= ~BUF_FLAG_PARSER; |
| mutex_unlock(&esparser_mutex); |
| return r; |
| } |
| EXPORT_SYMBOL(esparser_init); |
| |
| void esparser_audio_reset_s(struct stream_buf_s *buf) |
| { |
| ulong flags; |
| DEFINE_SPINLOCK(lock); |
| |
| spin_lock_irqsave(&lock, flags); |
| |
| SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL); |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3); |
| SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); |
| WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP, |
| READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR)); |
| |
| buf->flag |= BUF_FLAG_PARSER; |
| |
| audio_data_parsed = 0; |
| audio_real_wp = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR); |
| audio_buf_start = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR); |
| audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR); |
| spin_unlock_irqrestore(&lock, flags); |
| |
| return; |
| } |
| |
| void esparser_audio_reset(struct stream_buf_s *buf) |
| { |
| 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); |
| |
| buf->flag |= BUF_FLAG_PARSER; |
| |
| audio_data_parsed = 0; |
| audio_real_wp = 0; |
| audio_buf_start = 0; |
| audio_buf_end = 0; |
| spin_unlock_irqrestore(&lock, flags); |
| |
| } |
| |
| void esparser_release(struct stream_buf_s *buf) |
| { |
| u32 pts_type; |
| |
| /* check if esparser_init() is ever called */ |
| if ((buf->flag & BUF_FLAG_PARSER) == 0) |
| return; |
| |
| if (atomic_read(&esparser_use_count) == 0) { |
| pr_info |
| ("[%s:%d]###warning, esparser has been released already\n", |
| __func__, __LINE__); |
| return; |
| } |
| if (buf->write_thread) |
| threadrw_release(buf); |
| if (atomic_dec_and_test(&esparser_use_count)) { |
| WRITE_PARSER_REG(PARSER_INT_ENABLE, 0); |
| /*TODO irq */ |
| |
| vdec_free_irq(PARSER_IRQ, (void *)esparser_id); |
| |
| if (search_pattern) { |
| dma_unmap_single(amports_get_dma_device(), |
| search_pattern_map, |
| SEARCH_PATTERN_LEN, DMA_TO_DEVICE); |
| kfree(search_pattern); |
| search_pattern = NULL; |
| } |
| } |
| |
| if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC)) |
| pts_type = PTS_TYPE_VIDEO; |
| else if (buf->type == BUF_TYPE_VIDEO) |
| pts_type = PTS_TYPE_VIDEO; |
| else if (buf->type == BUF_TYPE_AUDIO) |
| pts_type = PTS_TYPE_AUDIO; |
| else if (buf->type == BUF_TYPE_SUBTITLE) { |
| buf->flag &= ~BUF_FLAG_PARSER; |
| return; |
| } else |
| return; |
| |
| buf->flag &= ~BUF_FLAG_PARSER; |
| pts_stop(pts_type); |
| } |
| EXPORT_SYMBOL(esparser_release); |
| |
| ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf, |
| const char __user *buf, size_t count) |
| { |
| s32 r; |
| u32 len; |
| u32 realcount, totalcount; |
| u32 havewritebytes = 0; |
| u32 leftcount = 0; |
| |
| struct drm_info tmpmm; |
| struct drm_info *drm = &tmpmm; |
| u32 res = 0; |
| int drm_flag = 0; |
| unsigned long realbuf; |
| |
| if (buf == NULL || count == 0) |
| return -EINVAL; |
| if (stbuf->write_thread) { |
| r = threadrw_flush_buffers(stbuf); |
| if (r < 0) |
| pr_info("Warning. drm flush threadrw failed[%d]\n", r); |
| } |
| res = copy_from_user(drm, buf, sizeof(struct drm_info)); |
| if (res) { |
| pr_info("drm kmalloc failed res[%d]\n", res); |
| return -EFAULT; |
| } |
| |
| if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) { |
| if (drm->drm_pktsize > MAX_DRM_PACKAGE_SIZE) { |
| pr_err("drm package size is error, size is %u\n", drm->drm_pktsize); |
| return -EINVAL; |
| } |
| /* buf only has drminfo not have esdata; */ |
| realbuf = drm->drm_phy; |
| realcount = drm->drm_pktsize; |
| drm_flag = drm->drm_flag; |
| /* DRM_PRNT("drm_get_rawdata |
| *onlydrminfo drm->drm_hasesdata[0x%x] |
| * stbuf->type %d buf[0x%x]\n", |
| *drm->drm_hasesdata,stbuf->type,buf); |
| */ |
| } else if (drm->drm_hasesdata == 1) { /* buf is drminfo+es; */ |
| if (drm->drm_pktsize > MAX_DRM_PACKAGE_SIZE) { |
| pr_err("drm package size is error, size is %u\n", drm->drm_pktsize); |
| return -EINVAL; |
| } |
| realcount = drm->drm_pktsize; |
| realbuf = (unsigned long)buf + sizeof(struct drm_info); |
| drm_flag = 0; |
| /* DRM_PRNT("drm_get_rawdata |
| * drminfo+es drm->drm_hasesdata[0x%x] |
| * stbuf->type %d\n",drm->drm_hasesdata,stbuf->type); |
| */ |
| } else { /* buf is hwhead; */ |
| realcount = count; |
| drm_flag = 0; |
| realbuf = (unsigned long)buf; |
| /* DRM_PRNT("drm_get_rawdata |
| * drm->drm_hasesdata[0x%x] |
| * len[%d] count[%d] realcout[%d]\n", |
| * drm->drm_hasesdata,len,count,realcount); |
| */ |
| } |
| |
| len = realcount; |
| count = realcount; |
| totalcount = realcount; |
| stbuf->drm_flag = drm_flag; |
| stbuf->is_phybuf = drm_flag ? 1 : 0; |
| |
| while (len > 0) { |
| if (stbuf->type != BUF_TYPE_SUBTITLE |
| && stbuf_space(stbuf) < count) { |
| /*should not write partial data in drm mode*/ |
| r = stbuf_wait_space(stbuf, count); |
| if (r < 0) |
| return r; |
| if (stbuf_space(stbuf) < count) |
| return -EAGAIN; |
| } |
| len = min_t(u32, len, count); |
| |
| mutex_lock(&esparser_mutex); |
| |
| if (stbuf->type != BUF_TYPE_AUDIO) |
| r = _esparser_write((const char __user *)realbuf, len, |
| stbuf, drm_flag); |
| else |
| r = _esparser_write_s((const char __user *)realbuf, len, |
| stbuf); |
| if (r < 0) { |
| pr_info("drm_write _esparser_write failed [%d]\n", r); |
| mutex_unlock(&esparser_mutex); |
| return r; |
| } |
| havewritebytes += r; |
| leftcount = totalcount - havewritebytes; |
| if (havewritebytes == totalcount) { |
| |
| mutex_unlock(&esparser_mutex); |
| break; /* write ok; */ |
| } else if ((len > 0) && (havewritebytes < totalcount)) { |
| DRM_PRNT |
| ("d writebytes[%d] want[%d] total[%d] real[%d]\n", |
| havewritebytes, len, totalcount, realcount); |
| len = len - r; /* write again; */ |
| realbuf = realbuf + r; |
| } else { |
| pr_info |
| ("e writebytes[%d] want[%d] total[%d] real[%d]\n", |
| havewritebytes, len, totalcount, realcount); |
| } |
| mutex_unlock(&esparser_mutex); |
| } |
| |
| if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) { |
| havewritebytes = sizeof(struct drm_info); |
| } else if (drm->drm_hasesdata == 1) { |
| havewritebytes += sizeof(struct drm_info); |
| } |
| return havewritebytes; |
| } |
| EXPORT_SYMBOL(drm_write); |
| |
| /* |
| *flags: |
| *1:phy |
| *2:noblock |
| */ |
| ssize_t esparser_write_ex(struct file *file, |
| struct stream_buf_s *stbuf, |
| const char __user *buf, size_t count, |
| int flags) |
| { |
| |
| s32 r; |
| u32 len = count; |
| |
| if (buf == NULL || count == 0) |
| return -EINVAL; |
| |
| /*subtitle have no level to check, */ |
| if (stbuf->type != BUF_TYPE_SUBTITLE && stbuf_space(stbuf) < count) { |
| if ((flags & 2) || ((file != NULL) && |
| (file->f_flags & O_NONBLOCK))) { |
| len = stbuf_space(stbuf); |
| |
| if (len < 256) /* <1k.do eagain, */ |
| return -EAGAIN; |
| } else { |
| len = min(stbuf_canusesize(stbuf) / 8, len); |
| |
| if (stbuf_space(stbuf) < len) { |
| r = stbuf_wait_space(stbuf, len); |
| if (r < 0) |
| return r; |
| } |
| } |
| } |
| |
| stbuf->last_write_jiffies64 = jiffies_64; |
| |
| len = min_t(u32, len, count); |
| |
| mutex_lock(&esparser_mutex); |
| |
| if (flags & 1) |
| stbuf->is_phybuf = true; |
| |
| if (stbuf->type == BUF_TYPE_AUDIO) |
| r = _esparser_write_s(buf, len, stbuf); |
| else |
| r = _esparser_write(buf, len, stbuf, flags & 1); |
| |
| mutex_unlock(&esparser_mutex); |
| |
| return r; |
| } |
| ssize_t esparser_write(struct file *file, |
| struct stream_buf_s *stbuf, |
| const char __user *buf, size_t count) |
| { |
| if (stbuf->write_thread) { |
| ssize_t ret; |
| |
| ret = threadrw_write(file, stbuf, buf, count); |
| if (ret == -EAGAIN) { |
| u32 a, b; |
| int vdelay, adelay; |
| |
| if ((stbuf->type != BUF_TYPE_VIDEO) && |
| (stbuf->type != BUF_TYPE_HEVC)) |
| return ret; |
| if (stbuf->buf_size > (SZ_1M * 30) || |
| (threadrw_buffer_size(stbuf) > SZ_1M * 10) || |
| !threadrw_support_more_buffers(stbuf)) |
| return ret; |
| /*only chang buffer for video.*/ |
| vdelay = calculation_stream_delayed_ms( |
| PTS_TYPE_VIDEO, &a, &b); |
| adelay = calculation_stream_delayed_ms( |
| PTS_TYPE_AUDIO, &a, &b); |
| if ((vdelay > 100 && vdelay < 2000) && /*vdelay valid.*/ |
| ((vdelay < 500) ||/*video delay is short!*/ |
| (adelay > 0 && adelay < 1000))/*audio is low.*/ |
| ) { |
| /*on buffer fulled. |
| *if delay is less than 100ms we think errors, |
| *And we add more buffer on delay < 2s. |
| */ |
| int new_size = 2 * 1024 * 1024; |
| |
| threadrw_alloc_more_buffer_size( |
| stbuf, new_size); |
| } |
| } |
| return ret; |
| } |
| return esparser_write_ex(file, stbuf, buf, count, 0); |
| } |
| EXPORT_SYMBOL(esparser_write); |
| |
| void esparser_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); |
| } |
| |
| static int esparser_stbuf_init(struct stream_buf_s *stbuf, |
| struct vdec_s *vdec) |
| { |
| int ret = -1; |
| |
| ret = stbuf_init(stbuf, vdec); |
| if (ret) |
| goto out; |
| |
| ret = esparser_init(stbuf, vdec); |
| if (!ret) |
| stbuf->flag |= BUF_FLAG_IN_USE; |
| out: |
| return ret; |
| } |
| |
| static void esparser_stbuf_release(struct stream_buf_s *stbuf) |
| { |
| esparser_release(stbuf); |
| |
| stbuf_release(stbuf); |
| } |
| |
| static struct stream_buf_ops esparser_stbuf_ops = { |
| .init = esparser_stbuf_init, |
| .release = esparser_stbuf_release, |
| .write = esparser_stbuf_write, |
| .get_wp = parser_get_wp, |
| .set_wp = parser_set_wp, |
| .get_rp = parser_get_rp, |
| .set_rp = parser_set_rp, |
| }; |
| |
| struct stream_buf_ops *get_esparser_stbuf_ops(void) |
| { |
| return &esparser_stbuf_ops; |
| } |
| EXPORT_SYMBOL(get_esparser_stbuf_ops); |
| |