diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 60f7cfbd9f9b..65eaf4066149 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -131,6 +131,39 @@ static const struct dvb_dmx_video_patterns h264_non_idr = { DMX_IDX_H264_NON_IDR_START }; +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * I-Slice NAL idc = 3, NAL type = 5, 01100101 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_idr_islice = { + {0x00, 0x00, 0x01, 0x65, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_IDR_ISLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * P-Slice NAL idc = 2, NAL type = 1, 01000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_pslice = { + {0x00, 0x00, 0x01, 0x41, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_PSLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * B-Slice NAL idc = 0, NAL type = 1, 00000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_bslice = { + {0x00, 0x00, 0x01, 0x01, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_BSLICE_START +}; + static const struct dvb_dmx_video_patterns h264_non_access_unit_del = { {0x00, 0x00, 0x01, 0x09}, {0xFF, 0xFF, 0xFF, 0x1F}, @@ -1011,6 +1044,18 @@ static void dvb_dmx_process_pattern_result(struct dvb_demux_feed *feed, } else if (feed->prev_frame_type & DMX_IDX_H264_NON_IDR_START) { idx_event.type = DMX_IDX_H264_NON_IDR_END; frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_IDR_ISLICE_START) { + idx_event.type = DMX_IDX_H264_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_PSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_BSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; } else { idx_event.type = DMX_IDX_VC1_FRAME_END; frame_end_in_seq = DMX_IDX_VC1_FIRST_SEQ_FRAME_END; @@ -1848,6 +1893,15 @@ const struct dvb_dmx_video_patterns *dvb_dmx_get_pattern(u64 dmx_idx_pattern) case DMX_IDX_H264_NON_IDR_START: return &h264_non_idr; + case DMX_IDX_H264_IDR_ISLICE_START: + return &h264_idr_islice; + + case DMX_IDX_H264_NON_IDR_PSLICE_START: + return &h264_non_idr_pslice; + + case DMX_IDX_H264_NON_IDR_BSLICE_START: + return &h264_non_idr_bslice; + case DMX_IDX_H264_ACCESS_UNIT_DEL: return &h264_non_access_unit_del; @@ -1975,6 +2029,40 @@ static void dvb_dmx_init_idx_state(struct dvb_demux_feed *feed) feed->pattern_num++; } + /* H264 IDR ISlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_IDR_ISLICE_START | DMX_IDX_H264_IDR_END | + DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_IDR_ISLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR PSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_PSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_PSLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR BSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_BSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_BSLICE_START); + feed->pattern_num++; + } + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && (feed->idx_params.types & DMX_IDX_H264_ACCESS_UNIT_DEL)) { feed->patterns[feed->pattern_num] = @@ -3253,7 +3341,8 @@ static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) { struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); + /* 4 Demux Instances each with group of 5 pids */ + memcpy(pids, dvbdemux->pids, DMX_PES_OTHER*sizeof(u16)); return 0; } diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h index 779de7ed078a..10a4cbbba2e7 100644 --- a/drivers/media/dvb-core/dvb_demux.h +++ b/drivers/media/dvb-core/dvb_demux.h @@ -359,6 +359,31 @@ static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed) return 0; } +/** + * dvb_dmx_is_audio_feed - Returns whether the PES feed + * is audio one. + * + * @feed: The feed to be checked. + * + * Return 1 if feed is audio feed, 0 otherwise. + */ +static inline int dvb_dmx_is_audio_feed(struct dvb_demux_feed *feed) +{ + if (feed->type != DMX_TYPE_TS) + return 0; + + if (feed->ts_type & (~TS_DECODER)) + return 0; + + if ((feed->pes_type == DMX_PES_AUDIO0) || + (feed->pes_type == DMX_PES_AUDIO1) || + (feed->pes_type == DMX_PES_AUDIO2) || + (feed->pes_type == DMX_PES_AUDIO3)) + return 1; + + return 0; +} + /** * dvb_dmx_is_pcr_feed - Returns whether the PES feed * is PCR one. diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c index 275b8b90af05..b706d598e6b8 100644 --- a/drivers/media/platform/msm/broadcast/tspp.c +++ b/drivers/media/platform/msm/broadcast/tspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. 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 version 2 and @@ -45,6 +45,8 @@ #include #include #include /* tasklet */ +#include /* Timer */ +#include /* Timer */ /* * General defines @@ -100,6 +102,7 @@ #define TSIF_TEST_RESET_OFF (0x1c) #define TSIF_TEST_EXPORT_OFF (0x20) #define TSIF_TEST_CURRENT_OFF (0x24) +#define TSIF_TTS_CTL_OFF (0x38) #define TSIF_DATA_PORT_OFF (0x100) @@ -128,6 +131,12 @@ #define TSIF_STS_CTL_STOP BIT(3) #define TSIF_STS_CTL_START BIT(0) +/* bits for TSIF_TTS_CTRL register */ +#define TSIF_TTS_CTL_TTS_ENDIANNESS BIT(4) +#define TSIF_TTS_CTL_TTS_SOURCE BIT(3) +#define TSIF_TTS_CTL_TTS_LENGTH_1 BIT(1) +#define TSIF_TTS_CTL_TTS_LENGTH_0 BIT(0) + /* * TSPP register offsets */ @@ -255,6 +264,7 @@ static const struct debugfs_entry debugfs_tsif_regs[] = { {"test_export", S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF}, {"test_current", S_IRUGO, TSIF_TEST_CURRENT_OFF}, {"data_port", S_IRUSR, TSIF_DATA_PORT_OFF}, + {"tts_source", S_IRUSR | S_IWUSR, TSIF_TTS_CTL_OFF}, }; static const struct debugfs_entry debugfs_tspp_regs[] = { @@ -369,6 +379,8 @@ struct tspp_tsif_device { u32 stat_overflow; u32 stat_lost_sync; u32 stat_timeout; + enum tsif_tts_source tts_source; + u32 lpass_timer_enable; }; enum tspp_buf_state { @@ -477,6 +489,7 @@ struct tspp_device { /* pinctrl */ struct mutex mutex; struct tspp_pinctrl pinctrl; + unsigned int tts_source; /* Time stamp source type LPASS timer/TCR */ struct dentry *dent; struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)]; @@ -911,6 +924,8 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) { int start_hardware = 0; u32 ctl; + u32 tts_ctl; + int retval; if (tsif_device->ref_count == 0) { start_hardware = 1; @@ -949,19 +964,57 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) TSIF_STS_CTL_TEST_MODE; break; case TSPP_TSIF_MODE_1: - ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR; + ctl |= TSIF_STS_CTL_EN_TIME_LIM; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; case TSPP_TSIF_MODE_2: ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR | TSIF_STS_CTL_MODE_2; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; default: pr_warn("tspp: unknown tsif mode 0x%x", tsif_device->mode); } + /* Set 4bytes Time Stamp for TCR */ + if (tsif_device->tts_source == TSIF_TTS_LPASS_TIMER) { + if (tsif_device->lpass_timer_enable == 0) { + retval = avcs_core_open(); + if (retval < 0) { + pr_warn("tspp: avcs open fail:%d\n", + retval); + return retval; + } + retval = avcs_core_disable_power_collapse(1); + if (retval < 0) { + pr_warn("tspp: avcs power enable:%d\n", + retval); + return retval; + } + tsif_device->lpass_timer_enable = 1; + } + + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + tts_ctl = 0; + /* Set LPASS Timer TTS source */ + tts_ctl |= TSIF_TTS_CTL_TTS_SOURCE; + /* Set 4 byte TTS */ + tts_ctl |= TSIF_TTS_CTL_TTS_LENGTH_0; + + writel_relaxed(tts_ctl, tsif_device->base + + TSIF_TTS_CTL_OFF); + /* write TTS control register */ + wmb(); + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + } + writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF); + /* write Status control register */ + wmb(); writel_relaxed(tsif_device->time_limit, tsif_device->base + TSIF_TIME_LIMIT_OFF); /* assure register configuration is done before starting TSIF */ @@ -982,8 +1035,13 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device) { - if (tsif_device->ref_count == 0) + if (tsif_device->ref_count == 0) { + if (tsif_device->lpass_timer_enable == 1) { + if (avcs_core_disable_power_collapse(0) == 0) + tsif_device->lpass_timer_enable = 0; + } return; + } tsif_device->ref_count--; @@ -1113,6 +1171,7 @@ static int tspp_global_reset(struct tspp_device *pdev) pdev->tsif[i].data_inverse = 0; pdev->tsif[i].sync_inverse = 0; pdev->tsif[i].enable_inverse = 0; + pdev->tsif[i].lpass_timer_enable = 0; } writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST); /* assure state is reset before continuing with configuration */ @@ -1884,6 +1943,89 @@ int tspp_get_ref_clk_counter(u32 dev, enum tspp_source source, u32 *tcr_counter) } EXPORT_SYMBOL(tspp_get_ref_clk_counter); +/** + * tspp_get_lpass_time_counter - return the LPASS Timer counter value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @source: The TSIF source from which the counter should be read + * @tcr_counter: the value of TCR counter + * + * Return error status + * + * If source is neither TSIF 0 or TSIF1 0 is returned. + */ +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter) +{ + struct tspp_device *pdev; + struct tspp_tsif_device *tsif_device; + + if (!lpass_time_counter) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_lpass_time_counter: can't find device %i\n", + dev); + return -ENODEV; + } + + switch (source) { + case TSPP_SOURCE_TSIF0: + tsif_device = &pdev->tsif[0]; + break; + + case TSPP_SOURCE_TSIF1: + tsif_device = &pdev->tsif[1]; + break; + + default: + tsif_device = NULL; + break; + } + + if (tsif_device && tsif_device->ref_count) { + if (avcs_core_query_timer(lpass_time_counter) < 0) { + pr_err("tspp_get_lpass_time_counter: read error\n"); + *lpass_time_counter = 0; + return -ENETRESET; + } + } else + *lpass_time_counter = 0; + + return 0; +} +EXPORT_SYMBOL(tspp_get_lpass_time_counter); + +/** + * tspp_get_tts_source - Return the TTS source value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @tts_source:Updated TTS source type + * + * Return error status + * + */ +int tspp_get_tts_source(u32 dev, int *tts_source) +{ + struct tspp_device *pdev; + + if (tts_source == NULL) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_tts_source: can't find device %i\n", + dev); + return -ENODEV; + } + + *tts_source = pdev->tts_source; + + return 0; +} +EXPORT_SYMBOL(tspp_get_tts_source); + /** * tspp_add_filter - add a TSPP filter to a channel. * @@ -2891,6 +3033,20 @@ static int msm_tspp_probe(struct platform_device *pdev) goto err_irq; device->req_irqs = false; + /* Check whether AV timer time stamps are enabled */ + if (!of_property_read_u32(pdev->dev.of_node, "qcom,lpass-timer-tts", + &device->tts_source)) { + if (device->tts_source == 1) + device->tts_source = TSIF_TTS_LPASS_TIMER; + else + device->tts_source = TSIF_TTS_TCR; + } else { + device->tts_source = TSIF_TTS_TCR; + } + + for (i = 0; i < TSPP_TSIF_INSTANCES; i++) + device->tsif[i].tts_source = device->tts_source; + /* power management */ pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/msm/dvb/adapter/Makefile b/drivers/media/platform/msm/dvb/adapter/Makefile index f7da6b5b2f06..662bf99c4d7e 100644 --- a/drivers/media/platform/msm/dvb/adapter/Makefile +++ b/drivers/media/platform/msm/dvb/adapter/Makefile @@ -1,5 +1,6 @@ ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/platform/msm/dvb/include/ +ccflags-y += -Idrivers/media/platform/msm/dvb/demux/ obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o diff --git a/drivers/media/platform/msm/dvb/demux/Kconfig b/drivers/media/platform/msm/dvb/demux/Kconfig index 319e2ab2eb96..d160c820d190 100644 --- a/drivers/media/platform/msm/dvb/demux/Kconfig +++ b/drivers/media/platform/msm/dvb/demux/Kconfig @@ -44,3 +44,11 @@ choice TSPP hardware support. All demux tasks will be performed in SW. endchoice + +config DVB_MPQ_MEDIA_BOX_DEMUX + bool "Media box demux support" + depends on DVB_MPQ_DEMUX + + help + Use this option if your HW is Qualcomm media box and demux + support is required on that media box. diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index 0188637af85c..ef0ef1512211 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -64,6 +64,11 @@ static int video_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; module_param(video_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(video_nonsecure_ion_heap, "ION heap for non-secure video buffer allocation"); +/* ION heap IDs used for allocating audio output buffer */ +static int audio_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; +module_param(audio_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(audio_nonsecure_ion_heap, "ION heap for non-secure audio buffer allocation"); + /* Value of TS packet scramble bits field for even key */ static int mpq_sdmx_scramble_even = 0x2; module_param(mpq_sdmx_scramble_even, int, S_IRUGO | S_IWUSR); @@ -104,6 +109,25 @@ module_param(tsif_mode, int, S_IRUGO | S_IWUSR); static int clock_inv; module_param(clock_inv, int, S_IRUGO | S_IWUSR); +/* TSIF Timestamp source: 0 = TSIF Clock Reference, 1 = LPASS time counter */ +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + +/* Store all mpq feeds corresponding to 4 TS programs in a Transport Stream */ +static struct mpq_feed *store_mpq_audio_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static struct mpq_feed *store_mpq_video_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static int non_predicted_video_frame; +/* trigger video ES frame events on MPEG2 B frames and H264 non-IDR frames */ +#ifdef CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX +static int video_b_frame_events = 1; +#else +static int video_b_frame_events; +#endif + /* Global data-structure for managing demux devices */ static struct { @@ -147,6 +171,96 @@ int mpq_dmx_get_param_clock_inv(void) return clock_inv; } +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_video_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_video) { + case DMX_PES_VIDEO0: + if (store_mpq_video_feed[0] != NULL) { + feed_data = &store_mpq_video_feed[0]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO0_STREAM_IF; + } + break; + case DMX_PES_VIDEO1: + if (store_mpq_video_feed[1] != NULL) { + feed_data = &store_mpq_video_feed[1]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO1_STREAM_IF; + } + break; + case DMX_PES_VIDEO2: + if (store_mpq_video_feed[2] != NULL) { + feed_data = &store_mpq_video_feed[2]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO2_STREAM_IF; + } + break; + case DMX_PES_VIDEO3: + if (store_mpq_video_feed[3] != NULL) { + feed_data = &store_mpq_video_feed[3]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_video_streambuffer); + +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_audio_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_audio) { + case DMX_PES_AUDIO0: + if (store_mpq_audio_feed[0] != NULL) { + feed_data = &store_mpq_audio_feed[0]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + } + break; + case DMX_PES_AUDIO1: + if (store_mpq_audio_feed[1] != NULL) { + feed_data = &store_mpq_audio_feed[1]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + } + break; + case DMX_PES_AUDIO2: + if (store_mpq_audio_feed[2] != NULL) { + feed_data = &store_mpq_audio_feed[2]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + } + break; + case DMX_PES_AUDIO3: + if (store_mpq_audio_feed[3] != NULL) { + feed_data = &store_mpq_audio_feed[3]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_audio_streambuffer); + + + /* Check that PES header is valid and that it is a video PES */ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) { @@ -163,6 +277,25 @@ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) return 0; } +static int mpq_dmx_is_valid_audio_pes(struct pes_packet_header *pes_header) +{ + /* start-code valid? */ + if ((pes_header->packet_start_code_prefix_1 != 0) || + (pes_header->packet_start_code_prefix_2 != 0) || + (pes_header->packet_start_code_prefix_3 != 1)) + return -EINVAL; + + /* Note: AC3 stream ID = 0xBD */ + if (pes_header->stream_id == 0xBD) + return 0; + + /* stream_id is audio? */ /* 110x xxxx = Audio Stream IDs */ + if ((pes_header->stream_id & 0xE0) != 0xC0) + return -EINVAL; + + return 0; +} + /* Check if a framing pattern is a video frame pattern or a header pattern */ static inline int mpq_dmx_is_video_frame( enum dmx_video_codec codec, @@ -170,6 +303,10 @@ static inline int mpq_dmx_is_video_frame( { switch (codec) { case DMX_VIDEO_CODEC_MPEG2: + if (video_b_frame_events == 1) + if (pattern_type == DMX_IDX_MPEG_B_FRAME_START) + non_predicted_video_frame = 1; + if ((pattern_type == DMX_IDX_MPEG_I_FRAME_START) || (pattern_type == DMX_IDX_MPEG_P_FRAME_START) || (pattern_type == DMX_IDX_MPEG_B_FRAME_START)) @@ -177,9 +314,20 @@ static inline int mpq_dmx_is_video_frame( return 0; case DMX_VIDEO_CODEC_H264: - if ((pattern_type == DMX_IDX_H264_IDR_START) || - (pattern_type == DMX_IDX_H264_NON_IDR_START)) - return 1; + if (video_b_frame_events == 1) { + if (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START) + non_predicted_video_frame = 1; + + if ((pattern_type == DMX_IDX_H264_IDR_ISLICE_START) || + (pattern_type == + DMX_IDX_H264_NON_IDR_PSLICE_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START)) + return 1; + } else { + if ((pattern_type == DMX_IDX_H264_IDR_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_START)) + return 1; + } return 0; case DMX_VIDEO_CODEC_VC1: @@ -219,10 +367,23 @@ static inline int mpq_dmx_get_pattern_params( case DMX_VIDEO_CODEC_H264: patterns[0] = dvb_dmx_get_pattern(DMX_IDX_H264_SPS); patterns[1] = dvb_dmx_get_pattern(DMX_IDX_H264_PPS); - patterns[2] = dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START); - patterns[3] = dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START); - patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); - *patterns_num = 5; + if (video_b_frame_events != 1) { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_START); + patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 5; + } else { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_ISLICE_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_PSLICE_START); + patterns[4] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_BSLICE_START); + patterns[5] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 6; + } break; case DMX_VIDEO_CODEC_VC1: @@ -254,12 +415,20 @@ void mpq_dmx_update_decoder_stat(struct mpq_feed *mpq_feed) struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; enum mpq_adapter_stream_if idx; - if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) || - (mpq_feed->video_info.stream_interface > - MPQ_ADAPTER_VIDEO3_STREAM_IF)) + if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + !dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed)) return; - idx = mpq_feed->video_info.stream_interface; + if (dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->video_info.stream_interface <= + MPQ_ADAPTER_VIDEO3_STREAM_IF) + idx = mpq_feed->video_info.stream_interface; + else if (dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->audio_info.stream_interface <= + MPQ_ADAPTER_AUDIO3_STREAM_IF) + idx = mpq_feed->audio_info.stream_interface; + else + return; curr_time = current_kernel_time(); if (unlikely(!mpq_demux->decoder_stat[idx].out_count)) { @@ -703,6 +872,7 @@ int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func) mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE; mpq_demux->sdmx_eos = 0; mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT; + mpq_demux->ts_packet_timestamp_source = 0; if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) { MPQ_DVB_ERR_PRINT( @@ -1050,9 +1220,33 @@ int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie) mutex_unlock(&mpq_demux->mutex); return ret; - } + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + struct mpq_streambuffer *stream_buffer; + int ret; - /* else */ + mutex_lock(&mpq_demux->mutex); + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: invalid feed, feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + + return ret; + } MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", __func__, feed->pes_type); @@ -1383,6 +1577,295 @@ int mpq_dmx_flush_stream_buffer(struct dvb_demux_feed *feed) return ret; } +static int mpq_dmx_init_audio_internal_buffers( + struct mpq_demux *mpq_demux, + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs) +{ + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; + int ret = 0; + + MPQ_DVB_DBG_PRINT("%s: Internal audio decoder buffer allocation\n", + __func__); + + actual_buffer_size = dec_buffs->buffers_size; + actual_buffer_size += (SZ_4K - 1); + actual_buffer_size &= ~(SZ_4K - 1); + + temp_handle = ion_alloc(mpq_demux->ion_client, + actual_buffer_size, SZ_4K, + ION_HEAP(audio_nonsecure_ion_heap), + mpq_demux->decoder_alloc_flags); + + if (IS_ERR_OR_NULL(temp_handle)) { + ret = PTR_ERR(temp_handle); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto end; + } + + payload_buffer = ion_map_kernel(mpq_demux->ion_client, temp_handle); + + if (IS_ERR_OR_NULL(payload_buffer)) { + ret = PTR_ERR(payload_buffer); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to map audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto init_failed_free_payload_buffer; + } + feed_data->buffer_desc.decoder_buffers_num = 1; + feed_data->buffer_desc.ion_handle[0] = temp_handle; + feed_data->buffer_desc.desc[0].base = payload_buffer; + feed_data->buffer_desc.desc[0].size = actual_buffer_size; + feed_data->buffer_desc.desc[0].read_ptr = 0; + feed_data->buffer_desc.desc[0].write_ptr = 0; + feed_data->buffer_desc.desc[0].handle = + ion_share_dma_buf_fd(mpq_demux->ion_client, temp_handle); + if (IS_ERR_VALUE(feed_data->buffer_desc.desc[0].handle)) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to share audio payload buffer %d\n", + __func__, ret); + ret = -ENOMEM; + goto init_failed_unmap_payload_buffer; + } + + feed_data->buffer_desc.shared_file = fget( + feed_data->buffer_desc.desc[0].handle); + + return 0; + +init_failed_unmap_payload_buffer: + ion_unmap_kernel(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.desc[0].base = NULL; +init_failed_free_payload_buffer: + ion_free(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.ion_handle[0] = NULL; + feed_data->buffer_desc.desc[0].size = 0; + feed_data->buffer_desc.decoder_buffers_num = 0; + feed_data->buffer_desc.shared_file = NULL; +end: + return ret; +} + +static int mpq_dmx_init_audio_external_buffers( + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs, + struct ion_client *client) +{ + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; + int ret = 0; + int i; + + /* + * Payload buffer was allocated externally (through ION). + * Map the ion handles to kernel memory + */ + MPQ_DVB_DBG_PRINT("%s: External audio decoder buffer allocation\n", + __func__); + + actual_buffer_size = dec_buffs->buffers_size; + if (!dec_buffs->is_linear) { + MPQ_DVB_DBG_PRINT("%s: Ex. Ring-buffer\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = 1; + } else { + MPQ_DVB_DBG_PRINT("%s: Ex. Linear\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = + dec_buffs->buffers_num; + } + + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + ret = mpq_map_buffer_to_kernel( + client, + dec_buffs->handles[i], + &temp_handle, + &payload_buffer); + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: Failed mapping audio buffer %d\n", + __func__, i); + goto init_failed; + } + feed_data->buffer_desc.ion_handle[i] = temp_handle; + feed_data->buffer_desc.desc[i].base = payload_buffer; + feed_data->buffer_desc.desc[i].handle = + dec_buffs->handles[i]; + feed_data->buffer_desc.desc[i].size = + dec_buffs->buffers_size; + feed_data->buffer_desc.desc[i].read_ptr = 0; + feed_data->buffer_desc.desc[i].write_ptr = 0; + + MPQ_DVB_DBG_PRINT( + "%s: Audio Buffer #%d: base=0x%p, handle=%d, size=%d\n", + __func__, i, + feed_data->buffer_desc.desc[i].base, + feed_data->buffer_desc.desc[i].handle, + feed_data->buffer_desc.desc[i].size); + } + + return 0; + +init_failed: + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } + return ret; +} +static int mpq_dmx_init_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer) +{ + int ret; + void *packet_buffer = NULL; + struct mpq_demux *mpq_demux = feed->mpq_demux; + struct ion_client *client = mpq_demux->ion_client; + struct dmx_decoder_buffers *dec_buffs = NULL; + enum mpq_streambuffer_mode mode; + + dec_buffs = feed->dvb_demux_feed->feed.ts.decoder_buffers; + + /* Allocate packet buffer holding the meta-data */ + packet_buffer = vmalloc(AUDIO_META_DATA_BUFFER_SIZE); + + if (packet_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate packets buffer\n", __func__); + ret = -ENOMEM; + goto end; + } + + MPQ_DVB_DBG_PRINT("%s: dec_buffs: num=%d, size=%d, linear=%d\n", + __func__, dec_buffs->buffers_num, + dec_buffs->buffers_size, + dec_buffs->is_linear); + + if (dec_buffs->buffers_num == 0) + ret = mpq_dmx_init_audio_internal_buffers( + mpq_demux, feed_data, dec_buffs); + else + ret = mpq_dmx_init_audio_external_buffers( + feed_data, dec_buffs, client); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + mode = dec_buffs->is_linear ? MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR : + MPQ_STREAMBUFFER_BUFFER_MODE_RING; + ret = mpq_streambuffer_init( + feed_data->audio_buffer, + mode, + feed_data->buffer_desc.desc, + feed_data->buffer_desc.decoder_buffers_num, + packet_buffer, + AUDIO_META_DATA_BUFFER_SIZE); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + goto end; + + +init_failed_free_packet_buffer: + vfree(packet_buffer); +end: + return ret; +} + +static void mpq_dmx_release_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *audio_buffer, + struct ion_client *client) +{ + int buf_num = 0; + int i; + struct dmx_decoder_buffers *dec_buffs = + feed->dvb_demux_feed->feed.ts.decoder_buffers; + + mpq_adapter_unregister_stream_if(feed_data->stream_interface); + + mpq_streambuffer_terminate(audio_buffer); + + vfree(audio_buffer->packet_data.data); + + buf_num = feed_data->buffer_desc.decoder_buffers_num; + + for (i = 0; i < buf_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + + /* + * Un-share the buffer if kernel is the one that + * shared it. + */ + if (!dec_buffs->buffers_num && + feed_data->buffer_desc.shared_file) { + fput(feed_data->buffer_desc.shared_file); + feed_data->buffer_desc.shared_file = NULL; + } + + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } +} + +int mpq_dmx_flush_audio_stream_buffer(struct dvb_demux_feed *feed) +{ + struct mpq_feed *mpq_feed = feed->priv; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *sbuff; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: not a audio feed, feed type=%d\n", + __func__, feed->pes_type); + return 0; + } + + spin_lock(&feed_data->audio_buffer_lock); + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + MPQ_DVB_DBG_PRINT("%s: feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return -ENODEV; + } + + ret = mpq_streambuffer_flush(sbuff); + if (ret) + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer_flush failed, ret=%d\n", + __func__, ret); + + spin_unlock(&feed_data->audio_buffer_lock); + + return ret; +} + static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) { struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; @@ -1398,6 +1881,10 @@ static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) MPQ_DVB_DBG_PRINT("%s: flushing video buffer\n", __func__); ret = mpq_dmx_flush_stream_buffer(feed); + } else if (dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: flushing audio buffer\n", __func__); + + ret = mpq_dmx_flush_audio_stream_buffer(feed); } mutex_unlock(&demux->mutex); @@ -1437,21 +1924,25 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) /* Register the new stream-buffer interface to MPQ adapter */ switch (mpq_feed->dvb_demux_feed->pes_type) { case DMX_PES_VIDEO0: + store_mpq_video_feed[0] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO0_STREAM_IF; break; case DMX_PES_VIDEO1: + store_mpq_video_feed[1] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO1_STREAM_IF; break; case DMX_PES_VIDEO2: + store_mpq_video_feed[2] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO2_STREAM_IF; break; case DMX_PES_VIDEO3: + store_mpq_video_feed[3] = mpq_feed; feed_data->stream_interface = MPQ_ADAPTER_VIDEO3_STREAM_IF; break; @@ -1551,6 +2042,126 @@ init_failed_free_priv_data: return ret; } +/* Register the new stream-buffer interface to MPQ adapter */ +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed) +{ + int ret; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_streambuffer *stream_buffer; + + switch (mpq_feed->dvb_demux_feed->pes_type) { + case DMX_PES_AUDIO0: + store_mpq_audio_feed[0] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + break; + + case DMX_PES_AUDIO1: + store_mpq_audio_feed[1] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + break; + + case DMX_PES_AUDIO2: + store_mpq_audio_feed[2] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + break; + + case DMX_PES_AUDIO3: + store_mpq_audio_feed[3] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + break; + + default: + MPQ_DVB_ERR_PRINT( + "%s: Invalid pes type %d\n", + __func__, + mpq_feed->dvb_demux_feed->pes_type); + ret = -EINVAL; + goto init_failed_free_priv_data; + } + + /* make sure not occupied already */ + stream_buffer = NULL; + mpq_adapter_get_stream_if( + feed_data->stream_interface, + &stream_buffer); + if (stream_buffer != NULL) { + MPQ_DVB_ERR_PRINT( + "%s: Audio interface %d already occupied!\n", + __func__, feed_data->stream_interface); + ret = -EBUSY; + goto init_failed_free_priv_data; + } + + feed_data->audio_buffer = + &mpq_dmx_info.decoder_buffers[feed_data->stream_interface]; + + ret = mpq_dmx_init_audio_streambuffer( + mpq_feed, feed_data, feed_data->audio_buffer); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_streambuffer failed, err = %d\n", + __func__, ret); + goto init_failed_free_priv_data; + } + + ret = mpq_adapter_register_stream_if( + feed_data->stream_interface, + feed_data->audio_buffer); + + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_adapter_register_stream_if failed, err = %d\n", + __func__, ret); + goto init_failed_free_stream_buffer; + } + + spin_lock_init(&feed_data->audio_buffer_lock); + + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + feed_data->pes_header_offset = 0; + mpq_feed->dvb_demux_feed->pusi_seen = 0; + mpq_feed->dvb_demux_feed->peslen = 0; + feed_data->fullness_wait_cancel = 0; + mpq_streambuffer_get_data_rw_offset(feed_data->audio_buffer, NULL, + &feed_data->frame_offset); + feed_data->saved_pts_dts_info.pts_exist = 0; + feed_data->saved_pts_dts_info.dts_exist = 0; + feed_data->new_pts_dts_info.pts_exist = 0; + feed_data->new_pts_dts_info.dts_exist = 0; + feed_data->saved_info_used = 1; + feed_data->new_info_exists = 0; + feed_data->first_pts_dts_copy = 1; + feed_data->tei_errs = 0; + feed_data->last_continuity = -1; + feed_data->continuity_errs = 0; + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + + mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_sum = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_max = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors = 0; + + return 0; + +init_failed_free_stream_buffer: + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + feed_data->audio_buffer, mpq_demux->ion_client); + mpq_adapter_unregister_stream_if(feed_data->stream_interface); +init_failed_free_priv_data: + feed_data->audio_buffer = NULL; + return ret; +} + /** * mpq_dmx_terminate_video_feed - terminate video feed information * that was previously initialized in mpq_dmx_init_video_feed @@ -1563,11 +2174,12 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) { struct mpq_streambuffer *video_buffer; struct mpq_video_feed_info *feed_data; - struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_demux *mpq_demux; if (mpq_feed == NULL) return -EINVAL; + mpq_demux = mpq_feed->mpq_demux; feed_data = &mpq_feed->video_info; spin_lock(&feed_data->video_buffer_lock); @@ -1582,6 +2194,30 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) return 0; } +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer *audio_buffer; + struct mpq_audio_feed_info *feed_data; + struct mpq_demux *mpq_demux; + + if (mpq_feed == NULL) + return -EINVAL; + + mpq_demux = mpq_feed->mpq_demux; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + audio_buffer = feed_data->audio_buffer; + feed_data->audio_buffer = NULL; + wake_up_all(&audio_buffer->raw_data.queue); + spin_unlock(&feed_data->audio_buffer_lock); + + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + audio_buffer, mpq_demux->ion_client); + + return 0; +} + struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed) { struct dvb_demux_feed *tmp; @@ -1814,6 +2450,12 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_terminate_video_feed failed. ret = %d\n", __func__, ret); + } else if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_terminate_audio_feed(mpq_feed); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_terminate_audio_feed failed. ret = %d\n", + __func__, ret); } if (mpq_feed->sdmx_buf_handle) { @@ -1835,22 +2477,28 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) { + struct mpq_feed *mpq_feed; + if (dvb_dmx_is_video_feed(feed)) { - struct mpq_feed *mpq_feed; struct mpq_video_feed_info *feed_data; mpq_feed = feed->priv; feed_data = &mpq_feed->video_info; feed_data->fullness_wait_cancel = 0; + return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + feed_data->fullness_wait_cancel = 0; + return 0; } - /* else */ - MPQ_DVB_DBG_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", __func__, + feed->pes_type); return -EINVAL; } @@ -1864,7 +2512,7 @@ int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) * * Return 1 if required free bytes are available, 0 otherwise. */ -static inline int mpq_dmx_check_decoder_fullness( +static inline int mpq_dmx_check_video_decoder_fullness( struct mpq_streambuffer *sbuff, size_t required_space) { @@ -1888,6 +2536,29 @@ static inline int mpq_dmx_check_decoder_fullness( return (free >= required_space); } +static inline int mpq_dmx_check_audio_decoder_fullness( + struct mpq_streambuffer *sbuff, + size_t required_space) +{ + ssize_t free = mpq_streambuffer_data_free(sbuff); + ssize_t free_meta = mpq_streambuffer_metadata_free(sbuff); + + /* Verify meta-data buffer can contain at least 1 packet */ + if (free_meta < AUDIO_META_DATA_PACKET_SIZE) + return 0; + + /* + * For linear buffers, verify there's enough space for this TSP + * and an additional buffer is free, as framing might required one + * more buffer to be available. + */ + if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) + return (free >= required_space && + sbuff->pending_buffers_count < sbuff->buffers_num-1); + else + return (free >= required_space); /* Ring buffer mode */ +} + /** * Checks whether decoder's output buffer has free space * for specific number of bytes, if not, the function waits @@ -1943,7 +2614,8 @@ static int mpq_dmx_decoder_fullness_check( if ((feed_data->video_buffer != NULL) && (!feed_data->fullness_wait_cancel) && - (!mpq_dmx_check_decoder_fullness(sbuff, required_space))) { + (!mpq_dmx_check_video_decoder_fullness(sbuff, + required_space))) { DEFINE_WAIT(__wait); for (;;) { @@ -1952,7 +2624,7 @@ static int mpq_dmx_decoder_fullness_check( TASK_INTERRUPTIBLE); if (!feed_data->video_buffer || feed_data->fullness_wait_cancel || - mpq_dmx_check_decoder_fullness(sbuff, + mpq_dmx_check_video_decoder_fullness(sbuff, required_space)) break; @@ -1987,11 +2659,102 @@ static int mpq_dmx_decoder_fullness_check( return 0; } +static int mpq_dmx_audio_decoder_fullness_check( + struct dvb_demux_feed *feed, + size_t required_space, + int lock_feed) +{ + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_streambuffer *sbuff = NULL; + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", + __func__, + feed->pes_type); + return -EINVAL; + } + + if (lock_feed) { + mutex_lock(&mpq_demux->mutex); + } else if (!mutex_is_locked(&mpq_demux->mutex)) { + MPQ_DVB_ERR_PRINT( + "%s: Mutex should have been locked\n", + __func__); + return -EINVAL; + } + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n", + __func__); + return -EINVAL; + } + + if ((feed_data->audio_buffer != NULL) && + (!feed_data->fullness_wait_cancel) && + (!mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space))) { + DEFINE_WAIT(__wait); + + for (;;) { + prepare_to_wait(&sbuff->raw_data.queue, + &__wait, TASK_INTERRUPTIBLE); + if (!feed_data->audio_buffer || + feed_data->fullness_wait_cancel || + mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space)) + break; + + if (!signal_pending(current)) { + mutex_unlock(&mpq_demux->mutex); + schedule(); + mutex_lock(&mpq_demux->mutex); + continue; + } + + ret = -ERESTARTSYS; + break; + } + finish_wait(&sbuff->raw_data.queue, &__wait); + } + + if (ret < 0) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return ret; + } + + if ((feed_data->fullness_wait_cancel) || + (feed_data->audio_buffer == NULL)) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return 0; +} + int mpq_dmx_decoder_fullness_wait( struct dvb_demux_feed *feed, size_t required_space) { - return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + if (dvb_dmx_is_video_feed(feed)) + return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + else if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_audio_decoder_fullness_check(feed, + required_space, 1); + + return 0; } int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) @@ -2009,8 +2772,7 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) spin_lock(&feed_data->video_buffer_lock); if (feed_data->video_buffer == NULL) { MPQ_DVB_DBG_PRINT( - "%s: video_buffer released\n", - __func__); + "%s: video_buffer released\n", __func__); spin_unlock(&feed_data->video_buffer_lock); return 0; } @@ -2019,14 +2781,34 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) wake_up_all(&video_buff->queue); spin_unlock(&feed_data->video_buffer_lock); + return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_feed *mpq_feed; + struct mpq_audio_feed_info *feed_data; + struct dvb_ringbuffer *audio_buff; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + feed_data->fullness_wait_cancel = 1; + + spin_lock(&feed_data->audio_buffer_lock); + if (feed_data->audio_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + audio_buff = &feed_data->audio_buffer->raw_data; + wake_up_all(&audio_buff->queue); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; } - /* else */ MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); + "%s: Invalid feed type %d\n", __func__, feed->pes_type); return -EINVAL; } @@ -2087,6 +2869,62 @@ int mpq_dmx_parse_mandatory_pes_header( return 0; } +int mpq_dmx_parse_mandatory_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) { + left_size = + PES_MANDATORY_FIELDS_LEN - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have beginning of PES header */ + *bytes_avail -= left_size; + *ts_payload_offset += left_size; + + /* Make sure the PES packet is valid */ + if (mpq_dmx_is_valid_audio_pes(pes_header) < 0) { + /* + * Since the new PES header parsing + * failed, reset pusi_seen to drop all + * data until next PUSI + */ + feed->pusi_seen = 0; + feed_data->pes_header_offset = 0; + + MPQ_DVB_ERR_PRINT( + "%s: invalid packet\n", + __func__); + + return -EINVAL; + } + + feed_data->pes_header_left_bytes = + pes_header->pes_header_data_length; + } + + return 0; +} + static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data, struct pes_packet_header *pes_header) { @@ -2126,6 +2964,46 @@ static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data, feed_data->new_info_exists = 1; } +static inline void mpq_dmx_get_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header) +{ + struct dmx_pts_dts_info *info = &(feed_data->new_pts_dts_info); + + /* Get PTS/DTS information from PES header */ + + if ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3)) { + info->pts_exist = 1; + + info->pts = + ((u64)pes_header->pts_1 << 30) | + ((u64)pes_header->pts_2 << 22) | + ((u64)pes_header->pts_3 << 15) | + ((u64)pes_header->pts_4 << 7) | + (u64)pes_header->pts_5; + } else { + info->pts_exist = 0; + info->pts = 0; + } + + if (pes_header->pts_dts_flag == 3) { + info->dts_exist = 1; + + info->dts = + ((u64)pes_header->dts_1 << 30) | + ((u64)pes_header->dts_2 << 22) | + ((u64)pes_header->dts_3 << 15) | + ((u64)pes_header->dts_4 << 7) | + (u64)pes_header->dts_5; + } else { + info->dts_exist = 0; + info->dts = 0; + } + + feed_data->new_info_exists = 1; +} + int mpq_dmx_parse_remaining_pes_header( struct dvb_demux_feed *feed, struct mpq_video_feed_info *feed_data, @@ -2218,6 +3096,96 @@ int mpq_dmx_parse_remaining_pes_header( return 0; } +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + /* Remaining header bytes that need to be processed? */ + if (!feed_data->pes_header_left_bytes) + return 0; + + /* Did we capture the PTS value (if exists)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+5)) && + ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3))) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 5 - + feed_data->pes_header_offset; + + copy_len = + (left_size > *bytes_avail) ? *bytes_avail : left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the PTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Did we capture the DTS value (if exist)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+10)) && + (pes_header->pts_dts_flag == 3)) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 10 - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the DTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Any more header bytes?! */ + if (feed_data->pes_header_left_bytes >= *bytes_avail) { + feed_data->pes_header_left_bytes -= *bytes_avail; + return -EINVAL; + } + + /* get PTS/DTS information from PES header to be written later */ + mpq_dmx_get_audio_pts_dts(feed_data, pes_header); + + /* Got PES header, process payload */ + *bytes_avail -= feed_data->pes_header_left_bytes; + *ts_payload_offset += feed_data->pes_header_left_bytes; + feed_data->pes_header_left_bytes = 0; + + return 0; +} + static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, int current_continuity, int discontinuity_indicator) @@ -2249,6 +3217,37 @@ static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, feed_data->last_continuity = current_continuity; } +static void mpq_dmx_check_audio_continuity( + struct mpq_audio_feed_info *feed_data, + int current_continuity, + int discontinuity_indicator) +{ + const int max_continuity = 0x0F; /* 4 bits in the TS packet header */ + + /* sanity check */ + if (unlikely((current_continuity < 0) || + (current_continuity > max_continuity))) { + MPQ_DVB_DBG_PRINT( + "%s: received invalid continuity counter value %d\n", + __func__, current_continuity); + return; + } + + /* reset last continuity */ + if ((feed_data->last_continuity == -1) || (discontinuity_indicator)) { + feed_data->last_continuity = current_continuity; + return; + } + + /* check for continuity errors */ + if (current_continuity != + ((feed_data->last_continuity + 1) & max_continuity)) + feed_data->continuity_errs++; + + /* save for next time */ + feed_data->last_continuity = current_continuity; +} + static inline void mpq_dmx_prepare_es_event_data( struct mpq_streambuffer_packet_header *packet, struct mpq_adapter_video_meta_data *meta_data, @@ -2295,6 +3294,43 @@ static inline void mpq_dmx_prepare_es_event_data( feed_data->continuity_errs = 0; } +static inline void mpq_dmx_prepare_audio_es_event_data( + struct mpq_streambuffer_packet_header *packet, + struct mpq_adapter_audio_meta_data *meta_data, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer, + struct dmx_data_ready *data, + int cookie) +{ + struct dmx_pts_dts_info *pts_dts; + + pts_dts = &meta_data->info.pes.pts_dts_info; + data->buf.stc = meta_data->info.pes.stc; + + data->data_length = 0; + data->buf.handle = packet->raw_data_handle; + data->buf.cookie = cookie; + data->buf.offset = packet->raw_data_offset; + data->buf.len = packet->raw_data_len; + data->buf.pts_exists = pts_dts->pts_exist; + data->buf.pts = pts_dts->pts; + data->buf.dts_exists = pts_dts->dts_exist; + data->buf.dts = pts_dts->dts; + data->buf.tei_counter = feed_data->tei_errs; + data->buf.cont_err_counter = feed_data->continuity_errs; + data->buf.ts_packets_num = feed_data->ts_packets_num; + data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes; + data->status = DMX_OK_DECODER_BUF; + + MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie); + + /* reset counters */ + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + feed_data->tei_errs = 0; + feed_data->continuity_errs = 0; +} + static int mpq_sdmx_dvr_buffer_desc(struct mpq_demux *mpq_demux, struct sdmx_buff_descr *buf_desc) { @@ -2488,6 +3524,81 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, spin_unlock(&feed_data->video_buffer_lock); } +/* + * in audio handling although ES frames are send to decoder, close the + * pes packet + */ +static void mpq_dmx_decoder_audio_pes_closure(struct mpq_demux *mpq_demux, + struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer_packet_header packet; + struct mpq_streambuffer *stream_buffer; + struct mpq_adapter_audio_meta_data meta_data; + struct mpq_audio_feed_info *feed_data; + struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; + struct dmx_data_ready data; + int cookie; + + feed_data = &mpq_feed->audio_info; + + /* + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio + * buffers). + */ + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return; + } + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + */ + if ((feed->pusi_seen) && (feed_data->pes_header_left_bytes == 0)) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle(stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = feed_data->frame_offset; + packet.user_data_len = + sizeof(struct mpq_adapter_audio_meta_data); + + mpq_dmx_write_audio_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); + + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie >= 0) { + /* Save write offset where new PES will begin */ + mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, + &feed_data->frame_offset); + mpq_dmx_prepare_audio_es_event_data(&packet, &meta_data, + feed_data, stream_buffer, &data, cookie); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } else { + MPQ_DVB_ERR_PRINT( + "%s: mpq_sb_pkt_write failed, ret=%d\n", + __func__, cookie); + } + } + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + + spin_unlock(&feed_data->audio_buffer_lock); +} + static int mpq_dmx_process_video_packet_framing( struct dvb_demux_feed *feed, const u8 *buf, @@ -2824,6 +3935,7 @@ static int mpq_dmx_process_video_packet_framing( pending_data_len -= bytes_to_write; feed_data->pending_pattern_len += bytes_to_write; } + non_predicted_video_frame = 0; is_video_frame = mpq_dmx_is_video_frame( feed->video_codec, @@ -2863,9 +3975,9 @@ static int mpq_dmx_process_video_packet_framing( ret = mpq_streambuffer_pkt_write(stream_buffer, &packet, (u8 *)&meta_data); if (ret < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", - __func__, ret); + MPQ_DVB_ERR_PRINT + ("%s: mpq_sb_pkt_write failed ret=%d\n", + __func__, ret); if (ret == -ENOSPC) mpq_dmx_notify_overflow(feed); } else { @@ -2873,7 +3985,15 @@ static int mpq_dmx_process_video_packet_framing( &packet, &meta_data, feed_data, stream_buffer, &data, ret); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + /* Trigger ES Data Event for VPTS */ + if (video_b_frame_events == 1) { + if (non_predicted_video_frame == 1) + feed->data_ready_cb.ts + (&feed->feed.ts, &data); + } else { + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } if (feed_data->video_buffer->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) @@ -3071,8 +4191,8 @@ static int mpq_dmx_process_video_packet_no_framing( stream_buffer, &packet, (u8 *)&meta_data); if (cookie < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", __func__, cookie); } else { /* @@ -3200,60 +4320,335 @@ static int mpq_dmx_process_video_packet_no_framing( return 0; } +/* + * parse PES headers and send down ES packets to decoder + * Trigger a new ES Data Event with APTS and QTimer in 1st PES + */ +static int mpq_dmx_process_audio_packet_no_framing( + struct dvb_demux_feed *feed, + const u8 *buf, + u64 curr_stc) +{ + int bytes_avail; + u32 ts_payload_offset; + struct mpq_audio_feed_info *feed_data; + const struct ts_packet_header *ts_header; + struct mpq_streambuffer *stream_buffer; + struct pes_packet_header *pes_header; + struct mpq_demux *mpq_demux; + struct mpq_feed *mpq_feed; + int discontinuity_indicator = 0; + struct dmx_data_ready data; + int cookie; + int ret; + + mpq_demux = feed->demux->priv; + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + /* + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio + * buffers). Mutex on the audio-feed cannot be held here + * since SW demux holds a spin-lock while calling write_to_decoder + */ + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: audio_buffer released\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + ts_header = (const struct ts_packet_header *)buf; + + pes_header = &feed_data->pes_header; + + /* Make sure this TS packet has a payload and not scrambled */ + if ((ts_header->sync_byte != 0x47) || + (ts_header->adaptation_field_control == 0) || + (ts_header->adaptation_field_control == 2) || + (ts_header->transport_scrambling_control)) { + /* continue to next packet */ + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + if (ts_header->payload_unit_start_indicator) { /* PUSI? */ + if (feed->pusi_seen) { /* Did we see PUSI before? */ + struct mpq_streambuffer_packet_header packet; + struct mpq_adapter_audio_meta_data meta_data; + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + * Double check that we are not in middle of + * previous PES header parsing. + */ + + if (feed_data->pes_header_left_bytes == 0) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle( + stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = + feed_data->frame_offset; + packet.user_data_len = + sizeof(struct + mpq_adapter_audio_meta_data); + + mpq_dmx_write_audio_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + /* Mark that we detected start of new PES */ + feed_data->first_pts_dts_copy = 1; + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); + + /* actual writing of stream audio headers */ + cookie = mpq_streambuffer_pkt_write( + stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie < 0) { + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", + __func__, cookie); + } else { + /* + * Save write offset where new PES + * will begin + */ + mpq_streambuffer_get_data_rw_offset( + stream_buffer, + NULL, + &feed_data->frame_offset); + + mpq_dmx_prepare_audio_es_event_data( + &packet, &meta_data, + feed_data, + stream_buffer, &data, cookie); + + /* + * Trigger ES data event for APTS + * and AFRAME + */ + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } + } else { + MPQ_DVB_ERR_PRINT( + "%s: received PUSI while handling PES header of previous PES\n", + __func__); + } + + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = + PES_MANDATORY_FIELDS_LEN; + } else { + feed->pusi_seen = 1; + } + + feed_data->prev_stc = curr_stc; + } + + /* + * Parse PES data only if PUSI was encountered, + * otherwise the data is dropped + */ + if (!feed->pusi_seen) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; /* drop and wait for next packets */ + } + + ts_payload_offset = sizeof(struct ts_packet_header); + + /* + * Skip adaptation field if exists. + * Save discontinuity indicator if exists. + */ + if (ts_header->adaptation_field_control == 3) { + const struct ts_adaptation_field *adaptation_field = + (const struct ts_adaptation_field *)(buf + + ts_payload_offset); + + discontinuity_indicator = + adaptation_field->discontinuity_indicator; + ts_payload_offset += buf[ts_payload_offset] + 1; + } + + bytes_avail = TS_PACKET_SIZE - ts_payload_offset; + + /* The audio decoder requires ES packets ! */ + + /* Get the mandatory fields of the audio PES header */ + if (mpq_dmx_parse_mandatory_audio_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + if (mpq_dmx_parse_remaining_audio_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + /* + * If we reached here, + * then we are now at the PES payload data + */ + if (bytes_avail == 0) { + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + /* + * Need to back-up the PTS information + * of the start of new PES + */ + if (feed_data->first_pts_dts_copy) { + mpq_dmx_save_audio_pts_dts(feed_data); + feed_data->first_pts_dts_copy = 0; + } + + /* Update error counters based on TS header */ + feed_data->ts_packets_num++; + feed_data->tei_errs += ts_header->transport_error_indicator; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors += + ts_header->transport_error_indicator; + mpq_dmx_check_audio_continuity(feed_data, + ts_header->continuity_counter, + discontinuity_indicator); + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors += + feed_data->continuity_errs; + + /* actual writing of audio data for a stream */ + ret = mpq_streambuffer_data_write(stream_buffer, buf+ts_payload_offset, + bytes_avail); + if (ret < 0) { + mpq_demux->decoder_stat + [feed_data->stream_interface].drop_count += bytes_avail; + feed_data->ts_dropped_bytes += bytes_avail; + if (ret == -ENOSPC) + mpq_dmx_notify_overflow(feed); + } else { + feed->peslen += bytes_avail; + } + + spin_unlock(&feed_data->audio_buffer_lock); + + return 0; +} + +/* function ptr used in several places, handle differently */ int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed, struct dmx_buffer_status *dmx_buffer_status) { - struct mpq_demux *mpq_demux = feed->demux->priv; - struct mpq_video_feed_info *feed_data; - struct mpq_streambuffer *video_buff; - struct mpq_feed *mpq_feed; - if (!dvb_dmx_is_video_feed(feed)) { - MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); - return -EINVAL; - } + if (dvb_dmx_is_video_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_video_feed_info *feed_data; + struct mpq_streambuffer *video_buff; + struct mpq_feed *mpq_feed; - mutex_lock(&mpq_demux->mutex); + mutex_lock(&mpq_demux->mutex); + + mpq_feed = feed->priv; + feed_data = &mpq_feed->video_info; + video_buff = feed_data->video_buffer; + if (!video_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + dmx_buffer_status->error = video_buff->raw_data.error; + + if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + video_buff->buffers[0].size * + video_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + video_buff->buffers[0].size * + (video_buff->buffers_num - + video_buff->pending_buffers_count); + dmx_buffer_status->size = + video_buff->buffers[0].size * + video_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(video_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(video_buff); + dmx_buffer_status->size = video_buff->buffers[0].size; + } + + mpq_streambuffer_get_data_rw_offset( + video_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); - mpq_feed = feed->priv; - feed_data = &mpq_feed->video_info; - video_buff = feed_data->video_buffer; - if (!video_buff) { mutex_unlock(&mpq_demux->mutex); + + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_audio_feed_info *feed_data; + struct mpq_streambuffer *audio_buff; + struct mpq_feed *mpq_feed; + + mutex_lock(&mpq_demux->mutex); + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + audio_buff = feed_data->audio_buffer; + if (!audio_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + dmx_buffer_status->error = audio_buff->raw_data.error; + + if (audio_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + audio_buff->buffers[0].size * + audio_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + audio_buff->buffers[0].size * + (audio_buff->buffers_num - + audio_buff->pending_buffers_count); + dmx_buffer_status->size = + audio_buff->buffers[0].size * + audio_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(audio_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(audio_buff); + dmx_buffer_status->size = audio_buff->buffers[0].size; + } + + mpq_streambuffer_get_data_rw_offset( + audio_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); + + mutex_unlock(&mpq_demux->mutex); + } else { + MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", + __func__, feed->pes_type); return -EINVAL; } - - dmx_buffer_status->error = video_buff->raw_data.error; - - if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { - dmx_buffer_status->fullness = - video_buff->buffers[0].size * - video_buff->pending_buffers_count; - dmx_buffer_status->free_bytes = - video_buff->buffers[0].size * - (video_buff->buffers_num - - video_buff->pending_buffers_count); - dmx_buffer_status->size = - video_buff->buffers[0].size * - video_buff->buffers_num; - } else { - dmx_buffer_status->fullness = - mpq_streambuffer_data_avail(video_buff); - dmx_buffer_status->free_bytes = - mpq_streambuffer_data_free(video_buff); - dmx_buffer_status->size = video_buff->buffers[0].size; - } - - mpq_streambuffer_get_data_rw_offset( - video_buff, - &dmx_buffer_status->read_offset, - &dmx_buffer_status->write_offset); - - mutex_unlock(&mpq_demux->mutex); - return 0; } @@ -3268,10 +4663,18 @@ int mpq_dmx_process_video_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { curr_stc = 0; } else { - curr_stc = buf[STC_LOCATION_IDX + 2] << 16; - curr_stc += buf[STC_LOCATION_IDX + 1] << 8; - curr_stc += buf[STC_LOCATION_IDX]; - curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } } if (!video_framing) @@ -3282,6 +4685,34 @@ int mpq_dmx_process_video_packet( curr_stc); } +int mpq_dmx_process_audio_packet( + struct dvb_demux_feed *feed, + const u8 *buf) +{ + u64 curr_stc; + struct mpq_demux *mpq_demux = feed->demux->priv; + + if ((mpq_demux->source >= DMX_SOURCE_DVR0) && + (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { + curr_stc = 0; + } else { + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } + } + + return mpq_dmx_process_audio_packet_no_framing(feed, buf, curr_stc); +} + int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci) { const struct ts_packet_header *ts_header; @@ -3342,10 +4773,18 @@ int mpq_dmx_process_pcr_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { stc = 0; } else { - stc = buf[STC_LOCATION_IDX + 2] << 16; - stc += buf[STC_LOCATION_IDX + 1] << 8; - stc += buf[STC_LOCATION_IDX]; - stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + stc = buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + stc = buf[STC_LOCATION_IDX + 3] << 24; + stc += buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + } } data.data_length = 0; @@ -3356,45 +4795,86 @@ int mpq_dmx_process_pcr_packet( return 0; } -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed) +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type) { - struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; - struct mpq_streambuffer *stream_buffer; - struct mpq_streambuffer_packet_header oob_packet; - struct mpq_adapter_video_meta_data oob_meta_data; - int ret; + if (feed_type == 1) { /* video feed */ + struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_video_meta_data oob_meta_data; + int ret; - spin_lock(&feed_data->video_buffer_lock); - stream_buffer = feed_data->video_buffer; + spin_lock(&feed_data->video_buffer_lock); + stream_buffer = feed_data->video_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", + __func__); + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; + + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); - if (stream_buffer == NULL) { - MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__); spin_unlock(&feed_data->video_buffer_lock); - return 0; + return (ret < 0) ? ret : 0; + + } else if (feed_type == 2) { /* audio feed */ + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_audio_meta_data oob_meta_data; + int ret; + + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; + + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); + + spin_unlock(&feed_data->audio_buffer_lock); + return (ret < 0) ? ret : 0; } - memset(&oob_packet, 0, sizeof(oob_packet)); - oob_packet.user_data_len = sizeof(oob_meta_data); - oob_meta_data.packet_type = DMX_EOS_PACKET; - - ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, - (u8 *)&oob_meta_data); - - spin_unlock(&feed_data->video_buffer_lock); - return (ret < 0) ? ret : 0; + return 0; } void mpq_dmx_convert_tts(struct dvb_demux_feed *feed, const u8 timestamp[TIMESTAMP_LEN], u64 *timestampIn27Mhz) { + struct mpq_demux *mpq_demux = feed->demux->priv; + if (unlikely(!timestampIn27Mhz)) return; - *timestampIn27Mhz = timestamp[2] << 16; - *timestampIn27Mhz += timestamp[1] << 8; - *timestampIn27Mhz += timestamp[0]; - *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != TSIF_TTS_LPASS_TIMER) { + *timestampIn27Mhz = timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + *timestampIn27Mhz = timestamp[3] << 24; + *timestampIn27Mhz += timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + } } int mpq_sdmx_open_session(struct mpq_demux *mpq_demux) @@ -3904,6 +5384,16 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) } } + if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_init_audio_feed(mpq_feed); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_audio_feed failed, ret=%d\n", + __func__, ret); + goto init_mpq_feed_end; + } + } + /* * sdmx is not relevant for recording filters, which always use * regular filters (non-sdmx) @@ -3924,6 +5414,8 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) __func__, ret); if (dvb_dmx_is_video_feed(feed)) mpq_dmx_terminate_video_feed(mpq_feed); + else if (dvb_dmx_is_audio_feed(feed)) + mpq_dmx_terminate_audio_feed(mpq_feed); } init_mpq_feed_end: @@ -4662,7 +6154,7 @@ decoder_filter_check_flags: if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) { /* Notify decoder via the stream buffer */ - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Failed to notify decoder on EOS, ret=%d\n", @@ -5162,11 +6654,19 @@ int mpq_dmx_oob_command(struct dvb_demux_feed *feed, else mpq_dmx_decoder_frame_closure(mpq_demux, mpq_feed); - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Couldn't write oob eos packet\n", __func__); + } else if (dvb_dmx_is_audio_feed(feed)) { + mpq_dmx_decoder_audio_pes_closure(mpq_demux, + mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 2); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: Couldn't write oob eos packet\n", + __func__); } ret = feed->data_ready_cb.ts(&feed->feed.ts, &event); } else if (!mpq_demux->sdmx_eos) { diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h index f36e9e7e7a23..ca6372b7d152 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -44,12 +44,20 @@ #define VIDEO_META_DATA_BUFFER_SIZE \ (VIDEO_NUM_OF_PES_PACKETS * VIDEO_META_DATA_PACKET_SIZE) +#define AUDIO_NUM_OF_PES_PACKETS 100 + +#define AUDIO_META_DATA_PACKET_SIZE \ + (DVB_RINGBUFFER_PKTHDRSIZE + \ + sizeof(struct mpq_streambuffer_packet_header) + \ + sizeof(struct mpq_adapter_audio_meta_data)) + +#define AUDIO_META_DATA_BUFFER_SIZE \ + (AUDIO_NUM_OF_PES_PACKETS * AUDIO_META_DATA_PACKET_SIZE) + /* Max number open() request can be done on demux device */ #define MPQ_MAX_DMX_FILES 128 -/** - * TSIF alias name length - */ +/* TSIF alias name length */ #define TSIF_NAME_LENGTH 20 /** @@ -332,6 +340,30 @@ const struct dvb_dmx_video_patterns *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM]; u64 prev_stc; }; +/* require a bare minimal mpq_audio_feed_info struct */ +struct mpq_audio_feed_info { + struct mpq_streambuffer *audio_buffer; + spinlock_t audio_buffer_lock; + struct mpq_decoder_buffers_desc buffer_desc; + struct pes_packet_header pes_header; + u32 pes_header_left_bytes; + u32 pes_header_offset; + int fullness_wait_cancel; + enum mpq_adapter_stream_if stream_interface; + u32 frame_offset; /* pes frame offset */ + struct dmx_pts_dts_info saved_pts_dts_info; + struct dmx_pts_dts_info new_pts_dts_info; + int saved_info_used; + int new_info_exists; + int first_pts_dts_copy; + u32 tei_errs; + int last_continuity; + u32 continuity_errs; + u32 ts_packets_num; + u32 ts_dropped_bytes; + u64 prev_stc; +}; + /** * mpq feed object - mpq common plugin feed information * @@ -367,6 +399,7 @@ struct mpq_feed { struct ion_handle *sdmx_buf_handle; struct mpq_video_feed_info video_info; + struct mpq_audio_feed_info audio_info; }; /** @@ -509,6 +542,7 @@ struct mpq_demux { enum sdmx_log_level sdmx_log_level; struct timespec last_notification_time; + int ts_packet_timestamp_source; }; /** @@ -879,11 +913,12 @@ struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed); /** * mpq_dmx_decoder_eos_cmd() - Report EOS event to the mpq_streambuffer * - * @mpq_feed: Video mpq_feed object for notification + * @mpq_feed: Audio/Video mpq_feed object for notification + * @feed_type: Feed type( Audio or Video ) * * Return error code */ -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed); +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type); /** * mpq_dmx_parse_mandatory_pes_header() - Parse non-optional PES header fields @@ -1023,5 +1058,66 @@ int mpq_dmx_get_param_scramble_even(void); /* Return the common module parameter mpq_sdmx_scramble_default_discard */ int mpq_dmx_get_param_scramble_default_discard(void); +/* APIs for Audio stream buffers interface -- Added for broadcase use case */ +/* + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio); +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video); + +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail); + +static inline void mpq_dmx_save_audio_pts_dts( + struct mpq_audio_feed_info *feed_data) +{ + if (feed_data->new_info_exists) { + feed_data->saved_pts_dts_info.pts_exist = + feed_data->new_pts_dts_info.pts_exist; + feed_data->saved_pts_dts_info.pts = + feed_data->new_pts_dts_info.pts; + feed_data->saved_pts_dts_info.dts_exist = + feed_data->new_pts_dts_info.dts_exist; + feed_data->saved_pts_dts_info.dts = + feed_data->new_pts_dts_info.dts; + + feed_data->new_info_exists = 0; + feed_data->saved_info_used = 0; + } +} + +/* + * mpq_dmx_process_audio_packet - Assemble Audio PES data and output to + * stream buffer connected to decoder. + */ +int mpq_dmx_process_audio_packet(struct dvb_demux_feed *feed, const u8 *buf); + +static inline void mpq_dmx_write_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct dmx_pts_dts_info *info) +{ + if (!feed_data->saved_info_used) { + info->pts_exist = feed_data->saved_pts_dts_info.pts_exist; + info->pts = feed_data->saved_pts_dts_info.pts; + info->dts_exist = feed_data->saved_pts_dts_info.dts_exist; + info->dts = feed_data->saved_pts_dts_info.dts; + + feed_data->saved_info_used = 1; + } else { + info->pts_exist = 0; + info->dts_exist = 0; + } +} #endif /* _MPQ_DMX_PLUGIN_COMMON_H */ diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c index be88bc1bf19f..4f4b9170d639 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -1634,6 +1634,9 @@ static int mpq_tspp_dmx_write_to_decoder( if (dvb_dmx_is_video_feed(feed)) return mpq_dmx_process_video_packet(feed, buf); + if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_process_audio_packet(feed, buf); + if (dvb_dmx_is_pcr_feed(feed)) return mpq_dmx_process_pcr_packet(feed, buf); @@ -1663,7 +1666,7 @@ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING | - DMX_CAP_AUTO_BUFFER_FLUSH; + DMX_CAP_AUDIO_DECODER_DATA | DMX_CAP_AUTO_BUFFER_FLUSH; caps->recording_max_video_pids_indexed = 0; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; @@ -1753,6 +1756,8 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, { enum tspp_source source; u32 tcr_counter; + u64 avtimer_stc = 0; + int tts_source = 0; if (!demux || !stc || !base) return -EINVAL; @@ -1764,11 +1769,18 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, else return -EINVAL; - tspp_get_ref_clk_counter(0, source, &tcr_counter); - - *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ - *base = 300; /* divisor to get 90KHz clock from stc value */ + if (tspp_get_tts_source(0, &tts_source) < 0) + tts_source = TSIF_TTS_TCR; + if (tts_source != TSIF_TTS_LPASS_TIMER) { + tspp_get_ref_clk_counter(0, source, &tcr_counter); + *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ + *base = 300; /* divisor to get 90KHz clock from stc value */ + } else { + if (tspp_get_lpass_time_counter(0, source, &avtimer_stc) < 0) + return -EINVAL; + *stc = avtimer_stc; + } return 0; } @@ -1840,6 +1852,10 @@ static int mpq_tspp_dmx_init( /* Extend dvb-demux debugfs with TSPP statistics. */ mpq_dmx_init_debugfs_entries(mpq_demux); + /* Get the TSIF TTS info */ + if (tspp_get_tts_source(0, &mpq_demux->ts_packet_timestamp_source) < 0) + mpq_demux->ts_packet_timestamp_source = TSIF_TTS_TCR; + return 0; init_failed_dmx_release: diff --git a/drivers/media/platform/msm/dvb/include/mpq_adapter.h b/drivers/media/platform/msm/dvb/include/mpq_adapter.h index 54e671c3b38d..c55a5aa1ae32 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_adapter.h +++ b/drivers/media/platform/msm/dvb/include/mpq_adapter.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -27,12 +27,24 @@ enum mpq_adapter_stream_if { /** Interface holding stream-buffer for video1 stream */ MPQ_ADAPTER_VIDEO1_STREAM_IF = 1, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video2 stream */ MPQ_ADAPTER_VIDEO2_STREAM_IF = 2, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video3 stream */ MPQ_ADAPTER_VIDEO3_STREAM_IF = 3, + /** Interface holding stream-buffer for audio0 stream */ + MPQ_ADAPTER_AUDIO0_STREAM_IF = 4, + + /** Interface holding stream-buffer for audio1 stream */ + MPQ_ADAPTER_AUDIO1_STREAM_IF = 5, + + /** Interface holding stream-buffer for audio2 stream */ + MPQ_ADAPTER_AUDIO2_STREAM_IF = 6, + + /** Interface holding stream-buffer for audio3 stream */ + MPQ_ADAPTER_AUDIO3_STREAM_IF = 7, + /** Maximum number of interfaces holding stream-buffers */ MPQ_ADAPTER_MAX_NUM_OF_INTERFACES, }; @@ -113,6 +125,17 @@ struct mpq_adapter_video_meta_data { } info; } __packed; +/** The meta-data used for audio interface */ +struct mpq_adapter_audio_meta_data { + /** meta-data packet type */ + enum dmx_packet_type packet_type; + + /** packet-type specific information */ + union { + struct dmx_pes_packet_info pes; + struct dmx_marker_info marker; + } info; +} __packed; /** Callback function to notify on registrations of specific interfaces */ typedef void (*mpq_adapter_stream_if_callback)( diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h index b24dc1f3b5ff..62404513007a 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h +++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -15,7 +15,6 @@ #include "dvb_ringbuffer.h" - /** * DOC: MPQ Stream Buffer * @@ -459,4 +458,37 @@ ssize_t mpq_streambuffer_metadata_free(struct mpq_streambuffer *sbuff); */ int mpq_streambuffer_flush(struct mpq_streambuffer *sbuff); +/* + * ------------------------------------------------------ + * Consumer or AV Decoder Stream Interface to Ring Buffer + * ------------------------------------------------------ + * Producer is Demux Driver + * ------------------------ + * + * call from Audio/Video Decoder Driver to find Audio/Video + * streambuffer AV handles, "DMX_PES_AUDIO0 through 3" or + * DMX_PES_VIDEO0 through 3" interfaces corresponding to 4 programs. + */ + +/* call from Audio/Video Decoder Driver via POLLING to consume + * Headers and Compressed data from ring buffer using streambuffer handle. + * hdrdata[] and cdata[] buffers have to be malloc'd by consumer + * + * -------------------------- + * Consumer Calling Sequence + * -------------------------- + * Find the streambuffer corresponding to a DMX TS PES stream instance. + * 1. consumer_audio_streambuffer() or consumer_video_streambuffer() + * Process the packet headers if required. + * 2. mpq_read_new_packet_hdr_data() + * Process the compressed data by forwarding to AV decoder. + * 3. mpq_read_new_packet_compressed_data() + * Dispose the packet. + * 4. mpq_dispose_new_packet_read() + * + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ + #endif /* _MPQ_STREAM_BUFFER_H */ diff --git a/include/linux/qcom_tspp.h b/include/linux/qcom_tspp.h index 28e6695fb057..1b34c389d7f0 100644 --- a/include/linux/qcom_tspp.h +++ b/include/linux/qcom_tspp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and @@ -70,6 +70,11 @@ struct tspp_select_source { int enable_inverse; }; +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + typedef void (tspp_notifier)(int channel_id, void *user); typedef void* (tspp_allocator)(int channel_id, u32 size, phys_addr_t *phys_base, void *user); @@ -96,4 +101,8 @@ int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count, u32 size, u32 int_freq, tspp_allocator *alloc, tspp_memfree *memfree, void *user); +int tspp_get_tts_source(u32 dev, int *tts_source); +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter); + #endif /* _MSM_TSPP_H_ */ diff --git a/include/uapi/linux/dvb/dmx.h b/include/uapi/linux/dvb/dmx.h index a768696c90f8..175534a26792 100644 --- a/include/uapi/linux/dvb/dmx.h +++ b/include/uapi/linux/dvb/dmx.h @@ -148,6 +148,9 @@ enum dmx_video_codec { #define DMX_IDX_VC1_FRAME_END 0x02000000 #define DMX_IDX_H264_ACCESS_UNIT_DEL 0x04000000 #define DMX_IDX_H264_SEI 0x08000000 +#define DMX_IDX_H264_IDR_ISLICE_START 0x10000000 +#define DMX_IDX_H264_NON_IDR_PSLICE_START 0x20000000 +#define DMX_IDX_H264_NON_IDR_BSLICE_START 0x40000000 struct dmx_pes_filter_params {