summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-02-15 17:01:31 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-02-15 17:01:30 -0800
commit65ec0b0413671fd8be625f034ee9f9d2bbe9a4aa (patch)
tree3a5ba5a60dda6a291ce30424533e57e0b25faa2b
parent9f6ca698a1a02a31c0701dfcd6f95fa2a00d5364 (diff)
parent79db7c3476e4aa446c1a5c4d84eb275546d7ae91 (diff)
Merge "media: platform: msm: Add demux support for mediabox"
-rw-r--r--drivers/media/dvb-core/dvb_demux.c91
-rw-r--r--drivers/media/dvb-core/dvb_demux.h25
-rw-r--r--drivers/media/platform/msm/broadcast/tspp.c166
-rw-r--r--drivers/media/platform/msm/dvb/adapter/Makefile1
-rw-r--r--drivers/media/platform/msm/dvb/demux/Kconfig8
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c1720
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h108
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c28
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_adapter.h29
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h36
-rw-r--r--include/linux/qcom_tspp.h11
-rw-r--r--include/uapi/linux/dvb/dmx.h3
12 files changed, 2092 insertions, 134 deletions
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
@@ -360,6 +360,31 @@ static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed)
}
/**
+ * 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 <linux/string.h>
#include <linux/msm-bus.h>
#include <linux/interrupt.h> /* tasklet */
+#include <asm/arch_timer.h> /* Timer */
+#include <linux/avtimer_kernel.h> /* 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 */
@@ -1885,6 +1944,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.
*
* @dev: TSPP device (up to TSPP_MAX_DEVICES)
@@ -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,8 +2477,9 @@ 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;
@@ -1844,13 +2487,18 @@ int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
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;
}
@@ -2020,13 +2782,33 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
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;
}
-int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed,
- struct dmx_buffer_status *dmx_buffer_status)
+/*
+ * 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)
{
- struct mpq_demux *mpq_demux = feed->demux->priv;
- struct mpq_video_feed_info *feed_data;
- struct mpq_streambuffer *video_buff;
+ 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;
- if (!dvb_dmx_is_video_feed(feed)) {
- MPQ_DVB_ERR_PRINT(
- "%s: Invalid feed type %d\n",
- __func__,
- feed->pes_type);
- return -EINVAL;
+ 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;
}
- mutex_lock(&mpq_demux->mutex);
+ ts_header = (const struct ts_packet_header *)buf;
- 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;
+ 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;
}
- dmx_buffer_status->error = video_buff->raw_data.error;
+ 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);
- 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;
+ /* 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 {
- 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;
+ feed->peslen += bytes_avail;
}
- mpq_streambuffer_get_data_rw_offset(
- video_buff,
- &dmx_buffer_status->read_offset,
- &dmx_buffer_status->write_offset);
+ spin_unlock(&feed_data->audio_buffer_lock);
- mutex_unlock(&mpq_demux->mutex);
+ 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)
+{
+
+ 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);
+
+ 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);
+
+ 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;
+ }
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;
- memset(&oob_packet, 0, sizeof(oob_packet));
- oob_packet.user_data_len = sizeof(oob_meta_data);
- oob_meta_data.packet_type = DMX_EOS_PACKET;
+ } 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;
- ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet,
- (u8 *)&oob_meta_data);
+ spin_lock(&feed_data->audio_buffer_lock);
+ stream_buffer = feed_data->audio_buffer;
- spin_unlock(&feed_data->video_buffer_lock);
- return (ret < 0) ? ret : 0;
+ 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;
+ }
+
+ 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
{