Merge "msm: display: remove framebuffer memory associated with fb1 and fb2" into msm-3.0
diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c
index e7992f9..070f3bf 100644
--- a/arch/arm/mach-msm/board-8930-camera.c
+++ b/arch/arm/mach-msm/board-8930-camera.c
@@ -545,7 +545,7 @@
};
static struct msm_camera_sensor_platform_info sensor_board_info_s5k3l1yx = {
- .mount_angle = 0,
+ .mount_angle = 90,
.cam_vreg = msm_8930_s5k3l1yx_vreg,
.num_vreg = ARRAY_SIZE(msm_8930_s5k3l1yx_vreg),
.gpio_conf = &msm_8930_back_cam_gpio_conf,
@@ -572,6 +572,12 @@
msm_gpiomux_install(msm8930_cam_common_configs,
ARRAY_SIZE(msm8930_cam_common_configs));
+ if (machine_is_msm8930_cdp()) {
+ struct msm_camera_sensor_info *s_info;
+ s_info = &msm_camera_sensor_s5k3l1yx_data;
+ s_info->sensor_platform_info->mount_angle = 0;
+ }
+
platform_device_register(&msm_camera_server);
platform_device_register(&msm8960_device_csiphy0);
platform_device_register(&msm8960_device_csiphy1);
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index b128223..a8602d3 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -43,10 +43,11 @@
#include <mach/dma.h>
#include <mach/ion.h>
#include <mach/msm_memtypes.h>
+#include <mach/cpuidle.h>
+#include <mach/usb_bam.h>
#include "timer.h"
#include "devices.h"
#include "board-9615.h"
-#include <mach/cpuidle.h>
#include "pm.h"
#include "acpuclock.h"
#include "pm-boot.h"
@@ -568,6 +569,10 @@
.disable_reset_on_disconnect = true,
};
+static struct msm_hsic_peripheral_platform_data msm_hsic_peripheral_pdata = {
+ .keep_core_clk_on_suspend_workaround = true,
+};
+
#define PID_MAGIC_ID 0x71432909
#define SERIAL_NUM_MAGIC_ID 0x61945374
#define SERIAL_NUMBER_LENGTH 127
@@ -756,6 +761,8 @@
msm_device_otg.dev.platform_data = &msm_otg_pdata;
msm_otg_pdata.phy_init_seq = shelby_phy_init_seq;
+ msm_device_hsic_peripheral.dev.platform_data =
+ &msm_hsic_peripheral_pdata;
msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata;
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index 2de581b..c1915fb 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -1133,20 +1133,6 @@
if (rc < 0)
return rc;
- rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
- GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- GPIO_CFG_ENABLE);
- if (rc < 0) {
- pr_err("failed QRD3 GPIO_BACKLIGHT_EN tlmm config\n");
- return rc;
- }
- rc = gpio_direction_output(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
- if (rc < 0) {
- pr_err("failed to enable backlight\n");
- gpio_free(GPIO_QRD3_LCD_BACKLIGHT_EN);
- return rc;
- }
-
rc = gpio_request(GPIO_QRD3_LCD_EXT_2V85_EN,
"qrd3_gpio_ext_2v85_en");
if (rc < 0)
@@ -1191,6 +1177,20 @@
}
if (on) {
+ rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
+ GPIO_CFG_ENABLE);
+ if (rc < 0) {
+ pr_err("failed QRD3 GPIO_BACKLIGHT_EN tlmm config\n");
+ return rc;
+ }
+ rc = gpio_direction_output(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
+ if (rc < 0) {
+ pr_err("failed to enable backlight\n");
+ gpio_free(GPIO_QRD3_LCD_BACKLIGHT_EN);
+ return rc;
+ }
+
gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
udelay(190);
gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 0);
@@ -1198,8 +1198,11 @@
gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
/* 1 wire mode starts from this low to high transition */
udelay(50);
- } else
- gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, !!on);
+ } else {
+ gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
+ GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ GPIO_CFG_DISABLE);
+ }
gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_2V85_EN, !!on);
gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_1V8_EN, !!on);
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index ab52665..2cf3b8d 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -563,9 +563,9 @@
(DEC4_FORMAT),
/* Concurrency 6 */
- (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)),
- (DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
- 0, 0, 0,
+ (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|
+ (1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
+ 0, 0, 0, 0,
/* Concurrency 7 */
(DEC0_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 782bb9e..abc2cef 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -510,9 +510,9 @@
(DEC4_FORMAT),
/* Concurrency 6 */
- (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)),
- (DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
- 0, 0, 0,
+ (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|
+ (1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
+ 0, 0, 0, 0,
/* Concurrency 7 */
(DEC0_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
diff --git a/arch/arm/mach-msm/include/mach/qdss.h b/arch/arm/mach-msm/include/mach/qdss.h
index 3b236b8..05d8577 100644
--- a/arch/arm/mach-msm/include/mach/qdss.h
+++ b/arch/arm/mach-msm/include/mach/qdss.h
@@ -30,6 +30,7 @@
extern void qdss_put(struct qdss_source *src);
extern int qdss_enable(struct qdss_source *src);
extern void qdss_disable(struct qdss_source *src);
+extern void qdss_disable_sink(void);
extern int qdss_clk_enable(void);
extern void qdss_clk_disable(void);
#else
@@ -37,6 +38,7 @@
static inline void qdss_put(struct qdss_source *src) {}
static inline int qdss_enable(struct qdss_source *src) { return -ENOSYS; }
static inline void qdss_disable(struct qdss_source *src) {}
+static inline void qdss_disable_sink(void) {}
static inline int qdss_clk_enable(void) { return -ENOSYS; }
static inline void qdss_clk_disable(void) {}
#endif
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 4caa71b..ec135a3 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -14,6 +14,20 @@
#define _USB_BAM_H_
/**
+ * SPS Pipes direction.
+ *
+ * USB_TO_PEER_PERIPHERAL USB (as Producer) to other
+ * peer peripheral.
+ * PEER_PERIPHERAL_TO_USB Other Peripheral to
+ * USB (as consumer).
+ */
+enum usb_bam_pipe_dir {
+ USB_TO_PEER_PERIPHERAL,
+ PEER_PERIPHERAL_TO_USB,
+};
+
+#ifdef CONFIG_USB_BAM
+/**
* Connect USB-to-Periperal SPS connection.
*
* This function returns the allocated pipes number.
@@ -29,12 +43,31 @@
* @return 0 on success, negative value on error
*
*/
-#ifdef CONFIG_USB_BAM
int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx);
+
+/**
+ * Register a wakeup callback from peer BAM.
+ *
+ * @idx - Connection index.
+ *
+ * @callback - the callback function
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *), void* param);
#else
-int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+static inline int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+ return -ENODEV;
+}
+
+static inline int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *), void* param)
{
return -ENODEV;
}
#endif
#endif /* _USB_BAM_H_ */
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c
index 71e0409..6f5ccbf 100644
--- a/arch/arm/mach-msm/qdsp5/adsp.c
+++ b/arch/arm/mach-msm/qdsp5/adsp.c
@@ -34,6 +34,7 @@
#include <linux/wait.h>
#include <linux/wakelock.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <mach/debug_mm.h>
#include <linux/debugfs.h>
@@ -71,6 +72,10 @@
static uint32_t rpc_adsp_rtos_mtoa_vers_comp;
static DEFINE_MUTEX(adsp_open_lock);
+static struct workqueue_struct *msm_adsp_probe_work_queue;
+static void adsp_probe_work(struct work_struct *work);
+static DECLARE_WORK(msm_adsp_probe_work, adsp_probe_work);
+
/* protect interactions with the ADSP command/message queue */
static spinlock_t adsp_cmd_lock;
static spinlock_t adsp_write_lock;
@@ -1230,8 +1235,8 @@
goto fail_rpc_register;
}
- /* start the kernel thread to process the callbacks */
- kthread_run(adsp_rpc_thread, NULL, "kadspd");
+ /* schedule start of kernel thread later using work queue */
+ queue_work(msm_adsp_probe_work_queue, &msm_adsp_probe_work);
for (i = 0; i < count; i++) {
struct msm_adsp_module *mod = adsp_modules + i;
@@ -1271,6 +1276,13 @@
kfree(adsp_info.init_info_ptr);
return rc;
}
+
+static void adsp_probe_work(struct work_struct *work)
+{
+ /* start the kernel thread to process the callbacks */
+ kthread_run(adsp_rpc_thread, NULL, "kadspd");
+}
+
#ifdef CONFIG_DEBUG_FS
static int get_parameters(char *buf, long int *param1, int num_of_par)
{
@@ -1433,6 +1445,9 @@
rpc_adsp_rtos_mtoa_vers_comp = 0x00030001;
#endif
+ msm_adsp_probe_work_queue = create_workqueue("msm_adsp_probe");
+ if (msm_adsp_probe_work_queue == NULL)
+ return -ENOMEM;
msm_adsp_driver.driver.name = msm_adsp_driver_name;
rc = platform_driver_register(&msm_adsp_driver);
MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc);
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c
index de756eb..8cae321 100644
--- a/arch/arm/mach-msm/qdsp5/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp5/audio_aac.c
@@ -39,7 +39,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -126,8 +125,8 @@
/* ---- End of Host PCM section */
struct msm_adsp_module *audplay;
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
/* configuration to use on next enable */
uint32_t out_sample_rate;
@@ -1509,9 +1508,9 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audaac_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
mutex_unlock(&audio->lock);
#ifdef CONFIG_DEBUG_FS
@@ -1701,10 +1700,7 @@
MM_DBG("pmemsz = %d\n", pmem_sz);
audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
if (audio->phys) {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, pmem_sz,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, pmem_sz);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, \
freeing instance 0x%08x\n",
@@ -1715,7 +1711,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr \
0x%08x\n", audio->phys, (int)audio->data);
break;
@@ -1737,29 +1733,26 @@
MM_ERR("could not allocate read buffers, freeing instance \
0x%08x\n", (int)audio);
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
goto done;
}
- audio->map_v_read = msm_subsystem_map_buffer(
- audio->read_phys,
- PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_read = ioremap(audio->read_phys,
+ PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("could not map read buffers, freeing instance \
0x%08x\n", (int)audio);
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
free_contiguous_memory_by_paddr(audio->read_phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
goto done;
}
- audio->read_data = audio->map_v_read->vaddr;
+ audio->read_data = audio->map_v_read;
MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->read_phys, (int)audio->read_data);
@@ -1880,9 +1873,9 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac_in.c b/arch/arm/mach-msm/qdsp5/audio_aac_in.c
index 456a8ff..ab9f15c 100644
--- a/arch/arm/mach-msm/qdsp5/audio_aac_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_aac_in.c
@@ -40,7 +40,6 @@
#include <mach/msm_rpcrouter.h>
#include <mach/msm_memtypes.h>
#include <mach/iommu.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/iommu_domains.h>
#include <mach/msm_adsp.h>
@@ -141,8 +140,8 @@
/* data allocated for various buffers */
char *data;
dma_addr_t phys;
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int opened;
int enabled;
@@ -1249,13 +1248,13 @@
if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
(audio->out_data)) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->out_phys);
audio->out_data = NULL;
}
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
audio->data = NULL;
}
@@ -1355,16 +1354,15 @@
audio->phys = allocate_contiguous_ebi_nomap(dma_size, SZ_4K);
if (audio->phys) {
- audio->map_v_read = msm_subsystem_map_buffer(
- audio->phys, dma_size,
- MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+ audio->map_v_read = ioremap(
+ audio->phys, dma_size);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("could not map DMA buffers\n");
rc = -ENOMEM;
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
}
- audio->data = audio->map_v_read->vaddr;
+ audio->data = audio->map_v_read;
} else {
MM_ERR("could not allocate read buffers\n");
rc = -ENOMEM;
@@ -1380,23 +1378,22 @@
if (!audio->out_phys) {
MM_ERR("could not allocate write buffers\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->out_phys, BUFFER_SIZE,
- MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->out_phys, BUFFER_SIZE);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write phys address\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
free_contiguous_memory_by_paddr(\
audio->out_phys);
goto evt_error;
}
- audio->out_data = audio->map_v_write->vaddr;
+ audio->out_data = audio->map_v_write;
MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->out_phys, (int)audio->out_data);
}
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
index d66a270..1ad941f 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
@@ -47,7 +47,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -138,8 +137,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
@@ -979,12 +978,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("failed to map read buf\n");
rc = -ENOMEM;
@@ -994,7 +991,7 @@
uint8_t index;
uint32_t offset = 0;
audio->read_data =
- audio->map_v_read->vaddr;
+ audio->map_v_read;
audio->buf_refresh = 0;
audio->pcm_buf_count =
config.buffer_count;
@@ -1298,10 +1295,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audamrnb_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1494,10 +1491,8 @@
kfree(audio);
goto done;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, DMASZ,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->phys, DMASZ);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, freeing \
instance 0x%08x freeing\n", (int)audio);
@@ -1507,7 +1502,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr \
0x%08x\n", audio->phys, (int)audio->data);
}
@@ -1595,7 +1590,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
index 2b4ead9..d4e4893 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
@@ -2,7 +2,7 @@
*
* amrnb encoder device
*
- * Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009, 2011-2012 Code Aurora Forum. All rights reserved.
*
* This code is based in part on arch/arm/mach-msm/qdsp5/audio_in.c, which is
* Copyright (C) 2008 Google, Inc.
@@ -44,7 +44,6 @@
#include <mach/msm_rpcrouter.h>
#include <mach/msm_memtypes.h>
#include <mach/iommu.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/iommu_domains.h>
#include <mach/msm_adsp.h>
@@ -146,7 +145,7 @@
/* data allocated for various buffers */
char *data;
dma_addr_t phys;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_write;
uint8_t opened;
uint8_t enabled;
@@ -1206,7 +1205,7 @@
audio->opened = 0;
if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
(audio->out_data)) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->out_phys);
audio->out_data = NULL;
}
@@ -1330,9 +1329,8 @@
dma_size, audio->data, audio->phys);
goto evt_error;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->out_phys, BUFFER_SIZE,
- MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->out_phys, BUFFER_SIZE);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write phys address\n");
rc = -ENOMEM;
@@ -1342,7 +1340,7 @@
audio->out_phys);
goto evt_error;
}
- audio->out_data = audio->map_v_write->vaddr;
+ audio->out_data = audio->map_v_write;
MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->out_phys,
(uint32_t)audio->out_data);
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrwb.c b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
index b566c60..0d3b67a 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrwb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
@@ -46,7 +46,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -139,8 +138,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
int rflush; /* Read flush */
int wflush; /* Write flush */
@@ -975,12 +974,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("failed to map mem for read buf\n");
rc = -ENOMEM;
@@ -989,7 +986,7 @@
} else {
uint8_t index;
uint32_t offset = 0;
- audio->read_data = audio->map_v_read->vaddr;
+ audio->read_data = audio->map_v_read;
audio->pcm_feedback = 1;
audio->buf_refresh = 0;
audio->pcm_buf_count =
@@ -1366,10 +1363,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audamrwb_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1557,10 +1554,7 @@
kfree(audio);
goto done;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, DMASZ,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, DMASZ);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, freeing \
@@ -1571,7 +1565,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->phys, (int)audio->data);
}
@@ -1664,7 +1658,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c
index 86035db..7479e36 100644
--- a/arch/arm/mach-msm/qdsp5/audio_evrc.c
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c
@@ -41,7 +41,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -135,8 +134,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
int rflush; /* Read flush */
@@ -965,12 +964,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("failed to map mem"
" for read buf\n");
@@ -981,7 +978,7 @@
uint8_t index;
uint32_t offset = 0;
audio->read_data =
- audio->map_v_read->vaddr;
+ audio->map_v_read;
audio->buf_refresh = 0;
audio->pcm_buf_count =
config.buffer_count;
@@ -1290,10 +1287,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audevrc_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1485,10 +1482,7 @@
kfree(audio);
goto done;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, DMASZ,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, DMASZ);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, freeing \
instance 0x%08x\n", (int)audio);
@@ -1498,7 +1492,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->phys, (int)audio->data);
}
@@ -1588,7 +1582,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
index 0bdbf5d..99ad02b 100644
--- a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
@@ -41,7 +41,6 @@
#include <mach/msm_rpcrouter.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include "audmgr.h"
@@ -144,8 +143,8 @@
char *data;
dma_addr_t phys;
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int opened;
int enabled;
@@ -1189,12 +1188,12 @@
audio->opened = 0;
if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
(audio->out_data)) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->out_phys);
audio->out_data = NULL;
}
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
audio->data = NULL;
}
@@ -1295,17 +1294,14 @@
rc = -ENOMEM;
goto evt_error;
} else {
- audio->map_v_read = msm_subsystem_map_buffer(
- audio->phys, dma_size,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_read = ioremap(audio->phys, dma_size);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("could not map physical address\n");
rc = -ENOMEM;
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
}
- audio->data = audio->map_v_read->vaddr;
+ audio->data = audio->map_v_read;
MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->phys, (int)audio->data);
}
@@ -1316,25 +1312,23 @@
if (!audio->out_phys) {
MM_ERR("could not allocate physical write buffers\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->out_phys, BUFFER_SIZE,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->out_phys, BUFFER_SIZE);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write phys address\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
free_contiguous_memory_by_paddr(\
audio->out_phys);
goto evt_error;
}
- audio->out_data = audio->map_v_write->vaddr;
+ audio->out_data = audio->map_v_write;
MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->out_phys, (int)audio->out_data);
}
diff --git a/arch/arm/mach-msm/qdsp5/audio_lpa.c b/arch/arm/mach-msm/qdsp5/audio_lpa.c
index dab53dc..8dfba0b 100644
--- a/arch/arm/mach-msm/qdsp5/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp5/audio_lpa.c
@@ -46,7 +46,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -1221,7 +1220,7 @@
audpcm_reset_event_queue(audio);
MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
}
mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c
index f6fa62a..5abdf85 100644
--- a/arch/arm/mach-msm/qdsp5/audio_mp3.c
+++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c
@@ -39,7 +39,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/msm_memtypes.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
@@ -202,8 +201,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
uint32_t drv_status;
int mfield; /* meta field embedded in data */
@@ -1488,12 +1487,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("map of read buf failed\n");
@@ -1504,7 +1501,7 @@
uint8_t index;
uint32_t offset = 0;
audio->read_data =
- audio->map_v_read->vaddr;
+ audio->map_v_read;
audio->buf_refresh = 0;
audio->pcm_buf_count =
config.buffer_count;
@@ -1979,11 +1976,11 @@
audmp3_reset_event_queue(audio);
MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
}
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -2178,10 +2175,8 @@
audio->phys = allocate_contiguous_ebi_nomap(pmem_sz,
SZ_4K);
if (audio->phys) {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, pmem_sz,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->phys, pmem_sz);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write \
buffers, freeing instance \
@@ -2193,7 +2188,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr\
0x%08x\n", audio->phys,\
(int)audio->data);
@@ -2321,7 +2316,7 @@
return rc;
err:
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
}
audpp_adec_free(audio->dec_id);
diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm.c b/arch/arm/mach-msm/qdsp5/audio_pcm.c
index 6468e93..5cd95f6 100644
--- a/arch/arm/mach-msm/qdsp5/audio_pcm.c
+++ b/arch/arm/mach-msm/qdsp5/audio_pcm.c
@@ -46,7 +46,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -187,7 +186,7 @@
/* data allocated for various buffers */
char *data;
int32_t phys;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_write;
uint32_t drv_status;
int wflush; /* Write flush */
@@ -1379,7 +1378,7 @@
audpcm_reset_event_queue(audio);
MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
}
mutex_unlock(&audio->lock);
@@ -1551,10 +1550,8 @@
audio->phys = allocate_contiguous_ebi_nomap(pmem_sz,
SZ_4K);
if (audio->phys) {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, pmem_sz,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->phys, pmem_sz);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write\
buffers\n");
@@ -1567,7 +1564,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr\
0x%08x\n", audio->phys,\
(int)audio->data);
@@ -1678,7 +1675,7 @@
return rc;
err:
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
}
audpp_adec_free(audio->dec_id);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
index 2be5144..667a164 100644
--- a/arch/arm/mach-msm/qdsp5/audio_qcelp.c
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
@@ -42,7 +42,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -132,8 +131,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
int rflush; /* Read flush */
int wflush; /* Write flush */
@@ -966,12 +965,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("failed to map read buf\n");
rc = -ENOMEM;
@@ -981,7 +978,7 @@
uint8_t index;
uint32_t offset = 0;
audio->read_data =
- audio->map_v_read->vaddr;
+ audio->map_v_read;
audio->buf_refresh = 0;
audio->pcm_buf_count =
config.buffer_count;
@@ -1290,10 +1287,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audqcelp_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1483,10 +1480,7 @@
kfree(audio);
goto done;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, DMASZ,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, DMASZ);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, freeing \
instance 0x%08x\n", (int)audio);
@@ -1496,7 +1490,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->phys, (int)audio->data);
}
@@ -1586,7 +1580,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
index a79f721..6ebd5f9 100644
--- a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
@@ -42,7 +42,6 @@
#include <mach/msm_rpcrouter.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include "audmgr.h"
#include <mach/qdsp5/qdsp5audpreproc.h>
@@ -145,8 +144,8 @@
char *data;
dma_addr_t phys;
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int opened;
int enabled;
@@ -1191,13 +1190,13 @@
if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
(audio->out_data)) {
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->out_phys);
audio->out_data = NULL;
}
if (audio->data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
audio->data = NULL;
}
@@ -1298,17 +1297,14 @@
rc = -ENOMEM;
goto evt_error;
} else {
- audio->map_v_read = msm_subsystem_map_buffer(
- audio->phys, dma_size,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_read = ioremap(audio->phys, dma_size);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("could not map physical address\n");
rc = -ENOMEM;
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
}
- audio->data = audio->map_v_read->vaddr;
+ audio->data = audio->map_v_read;
MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->phys, (int)audio->data);
}
@@ -1320,25 +1316,23 @@
if (!audio->out_phys) {
MM_ERR("could not allocate physical write buffers\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
goto evt_error;
} else {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->out_phys, BUFFER_SIZE,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(
+ audio->out_phys, BUFFER_SIZE);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write phys address\n");
rc = -ENOMEM;
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->phys);
free_contiguous_memory_by_paddr(\
audio->out_phys);
goto evt_error;
}
- audio->out_data = audio->map_v_write->vaddr;
+ audio->out_data = audio->map_v_write;
MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
audio->out_phys, (int)audio->out_data);
}
diff --git a/arch/arm/mach-msm/qdsp5/audio_wma.c b/arch/arm/mach-msm/qdsp5/audio_wma.c
index 674ee4f..d767916 100644
--- a/arch/arm/mach-msm/qdsp5/audio_wma.c
+++ b/arch/arm/mach-msm/qdsp5/audio_wma.c
@@ -46,7 +46,6 @@
#include <mach/msm_adsp.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include <mach/qdsp5/qdsp5audppcmdi.h>
#include <mach/qdsp5/qdsp5audppmsg.h>
#include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -145,8 +144,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
int rflush; /* Read flush */
@@ -1043,12 +1042,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("map of read buf failed\n");
rc = -ENOMEM;
@@ -1058,7 +1055,7 @@
uint8_t index;
uint32_t offset = 0;
audio->read_data =
- audio->map_v_read->vaddr;
+ audio->map_v_read;
audio->buf_refresh = 0;
audio->pcm_buf_count =
config.buffer_count;
@@ -1435,10 +1432,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audwma_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1630,10 +1627,7 @@
MM_DBG("pmemsz = %d\n", pmem_sz);
audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
if (audio->phys) {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, pmem_sz,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, pmem_sz);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, \
freeing instance 0x%08x\n",
@@ -1644,7 +1638,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr \
0x%08x\n", audio->phys, (int)audio->data);
break;
@@ -1753,7 +1747,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_wmapro.c b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
index c2a0b93..7fb08ff 100644
--- a/arch/arm/mach-msm/qdsp5/audio_wmapro.c
+++ b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
@@ -52,7 +52,6 @@
#include <mach/msm_memtypes.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
#include "audmgr.h"
@@ -144,8 +143,8 @@
/* data allocated for various buffers */
char *data;
int32_t phys; /* physical address of write buffer */
- struct msm_mapped_buffer *map_v_read;
- struct msm_mapped_buffer *map_v_write;
+ void *map_v_read;
+ void *map_v_write;
int mfield; /* meta field embedded in data */
int rflush; /* Read flush */
@@ -1040,12 +1039,10 @@
rc = -ENOMEM;
break;
}
- audio->map_v_read = msm_subsystem_map_buffer(
+ audio->map_v_read = ioremap(
audio->read_phys,
config.buffer_size *
- config.buffer_count,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ config.buffer_count);
if (IS_ERR(audio->map_v_read)) {
MM_ERR("map of read buf failed\n");
@@ -1055,8 +1052,7 @@
} else {
uint8_t index;
uint32_t offset = 0;
- audio->read_data =
- audio->map_v_read->vaddr;
+ audio->read_data = audio->map_v_read;
audio->pcm_feedback = 1;
audio->buf_refresh = 0;
audio->pcm_buf_count =
@@ -1432,10 +1428,10 @@
audio->event_abort = 1;
wake_up(&audio->event_wait);
audwmapro_reset_event_queue(audio);
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
if (audio->read_data) {
- msm_subsystem_unmap_buffer(audio->map_v_read);
+ iounmap(audio->map_v_read);
free_contiguous_memory_by_paddr(audio->read_phys);
}
mutex_unlock(&audio->lock);
@@ -1627,10 +1623,7 @@
MM_DBG("pmemsz = %d\n", pmem_sz);
audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
if (audio->phys) {
- audio->map_v_write = msm_subsystem_map_buffer(
- audio->phys, pmem_sz,
- MSM_SUBSYSTEM_MAP_KADDR,
- NULL, 0);
+ audio->map_v_write = ioremap(audio->phys, pmem_sz);
if (IS_ERR(audio->map_v_write)) {
MM_ERR("could not map write buffers, \
freeing instance 0x%08x\n",
@@ -1641,7 +1634,7 @@
kfree(audio);
goto done;
}
- audio->data = audio->map_v_write->vaddr;
+ audio->data = audio->map_v_write;
MM_DBG("write buf: phy addr 0x%08x kernel addr \
0x%08x\n", audio->phys, (int)audio->data);
break;
@@ -1741,7 +1734,7 @@
done:
return rc;
err:
- msm_subsystem_unmap_buffer(audio->map_v_write);
+ iounmap(audio->map_v_write);
free_contiguous_memory_by_paddr(audio->phys);
audpp_adec_free(audio->dec_id);
kfree(audio);
diff --git a/arch/arm/mach-msm/qdss-etb.c b/arch/arm/mach-msm/qdss-etb.c
index 96eba26..9b23766 100644
--- a/arch/arm/mach-msm/qdss-etb.c
+++ b/arch/arm/mach-msm/qdss-etb.c
@@ -69,7 +69,7 @@
void __iomem *base;
bool enabled;
bool reading;
- struct mutex mutex;
+ spinlock_t spinlock;
atomic_t in_use;
struct device *dev;
struct kobject *kobj;
@@ -100,11 +100,13 @@
void etb_enable(void)
{
- mutex_lock(&etb.mutex);
+ unsigned long flags;
+
+ spin_lock_irqsave(&etb.spinlock, flags);
__etb_enable();
etb.enabled = true;
dev_info(etb.dev, "ETB enabled\n");
- mutex_unlock(&etb.mutex);
+ spin_unlock_irqrestore(&etb.spinlock, flags);
}
static void __etb_disable(void)
@@ -137,11 +139,13 @@
void etb_disable(void)
{
- mutex_lock(&etb.mutex);
+ unsigned long flags;
+
+ spin_lock_irqsave(&etb.spinlock, flags);
__etb_disable();
etb.enabled = false;
dev_info(etb.dev, "ETB disabled\n");
- mutex_unlock(&etb.mutex);
+ spin_unlock_irqrestore(&etb.spinlock, flags);
}
static void __etb_dump(void)
@@ -200,7 +204,9 @@
void etb_dump(void)
{
- mutex_lock(&etb.mutex);
+ unsigned long flags;
+
+ spin_lock_irqsave(&etb.spinlock, flags);
if (etb.enabled) {
__etb_disable();
__etb_dump();
@@ -208,7 +214,7 @@
dev_info(etb.dev, "ETB dumped\n");
}
- mutex_unlock(&etb.mutex);
+ spin_unlock_irqrestore(&etb.spinlock, flags);
}
static int etb_open(struct inode *inode, struct file *file)
@@ -343,7 +349,7 @@
etb.dev = &pdev->dev;
- mutex_init(&etb.mutex);
+ spin_lock_init(&etb.spinlock);
ret = misc_register(&etb_misc);
if (ret)
@@ -363,7 +369,6 @@
err_alloc:
misc_deregister(&etb_misc);
err_misc:
- mutex_destroy(&etb.mutex);
iounmap(etb.base);
err_ioremap:
err_res:
@@ -378,7 +383,6 @@
etb_sysfs_exit();
kfree(etb.buf);
misc_deregister(&etb_misc);
- mutex_destroy(&etb.mutex);
iounmap(etb.base);
return 0;
diff --git a/arch/arm/mach-msm/qdss.c b/arch/arm/mach-msm/qdss.c
index cfe65ad..0cc3ca5 100644
--- a/arch/arm/mach-msm/qdss.c
+++ b/arch/arm/mach-msm/qdss.c
@@ -175,6 +175,26 @@
EXPORT_SYMBOL(qdss_disable);
/**
+ * qdss_disable_sink - force disable the current qdss sink(s)
+ *
+ * Force disable the current qdss sink(s) to stop the sink from accepting any
+ * trace generated subsequent to this call. This function should only be used
+ * as a way to stop the sink from getting polluted with trace data that is
+ * uninteresting after an event of interest has occured.
+ *
+ * CONTEXT:
+ * Can be called from atomic or non-atomic context.
+ */
+void qdss_disable_sink(void)
+{
+ if ((qdss.pdata)->afamily) {
+ etb_dump();
+ etb_disable();
+ }
+}
+EXPORT_SYMBOL(qdss_disable_sink);
+
+/**
* qdss_clk_enable - enable qdss clocks
*
* Enables qdss clocks via RPM if they aren't already enabled, otherwise
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index e11b119..4ba9e7f 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -382,18 +382,35 @@
}
EXPORT_SYMBOL(kgsl_mmu_start);
+static void mh_axi_error(struct kgsl_device *device, const char* type)
+{
+ unsigned int reg, gpu_err, phys_err, pt_base;
+
+ kgsl_regread(device, MH_AXI_ERROR, ®);
+ pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
+ /*
+ * Read gpu virtual and physical addresses that
+ * caused the error from the debug data.
+ */
+ kgsl_regwrite(device, MH_DEBUG_CTRL, 44);
+ kgsl_regread(device, MH_DEBUG_DATA, &gpu_err);
+ kgsl_regwrite(device, MH_DEBUG_CTRL, 45);
+ kgsl_regread(device, MH_DEBUG_DATA, &phys_err);
+ KGSL_MEM_CRIT(device,
+ "axi %s error: %08x pt %08x gpu %08x phys %08x\n",
+ type, reg, pt_base, gpu_err, phys_err);
+}
+
void kgsl_mh_intrcallback(struct kgsl_device *device)
{
unsigned int status = 0;
- unsigned int reg;
kgsl_regread(device, MH_INTERRUPT_STATUS, &status);
- kgsl_regread(device, MH_AXI_ERROR, ®);
if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR)
- KGSL_MEM_CRIT(device, "axi read error interrupt: %08x\n", reg);
+ mh_axi_error(device, "read");
if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
- KGSL_MEM_CRIT(device, "axi write error interrupt: %08x\n", reg);
+ mh_axi_error(device, "write");
if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT)
device->mmu.mmu_ops->mmu_pagefault(&device->mmu);
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index ee214c3..f41a4ef 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -84,4 +84,8 @@
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
+comment "Qualcomm MPQ adapter"
+ depends on ARCH_MSM && DVB_CORE
+source "drivers/media/dvb/mpq/Kconfig"
+
endif # DVB_CAPTURE_DRIVERS
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index a1a0875..68877c8 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -18,3 +18,5 @@
ngene/
obj-$(CONFIG_DVB_FIREDTV) += firewire/
+obj-$(CONFIG_DVB_MPQ) += mpq/
+
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index eb91fd8..ff0c9d8 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -7,6 +7,8 @@
* Copyright (c) 2000 Nokia Research Center
* Tampere, FINLAND
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -190,6 +192,14 @@
struct dmx_section_filter * source,
enum dmx_success success);
+typedef int (*dmx_ts_fullness) (
+ struct dmx_ts_feed *source,
+ int required_space);
+
+typedef int (*dmx_section_fullness) (
+ struct dmx_section_filter *source,
+ int required_space);
+
/*--------------------------------------------------------------------------*/
/* DVB Front-End */
/*--------------------------------------------------------------------------*/
@@ -247,7 +257,7 @@
void* priv; /* Pointer to private data of the API client */
int (*open) (struct dmx_demux* demux);
int (*close) (struct dmx_demux* demux);
- int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count);
+ int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
int (*allocate_ts_feed) (struct dmx_demux* demux,
struct dmx_ts_feed** feed,
dmx_ts_cb callback);
@@ -271,7 +281,20 @@
int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps);
- int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src);
+ int (*set_source) (struct dmx_demux *demux, const dmx_source_t *src);
+
+ int (*set_tsp_format) (struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format);
+
+ int (*set_tsp_out_format) (struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format);
+
+ int (*set_playback_mode) (struct dmx_demux *demux,
+ enum dmx_playback_mode_t mode,
+ dmx_ts_fullness ts_fullness_callback,
+ dmx_section_fullness sec_fullness_callback);
+
+ int (*write_cancel) (struct dmx_demux *demux);
int (*get_stc) (struct dmx_demux* demux, unsigned int num,
u64 *stc, unsigned int *base);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index e4b5c03..ed3f731 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -4,6 +4,8 @@
* Copyright (C) 2000 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -28,6 +30,7 @@
#include <linux/poll.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
+#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include "dmxdev.h"
@@ -81,12 +84,15 @@
break;
}
- ret = wait_event_interruptible(src->queue,
- !dvb_ringbuffer_empty(src) ||
- (src->error != 0));
+ ret = wait_event_interruptible(src->queue, (!src->data) ||
+ !dvb_ringbuffer_empty(src) ||
+ (src->error != 0));
if (ret < 0)
break;
+ if (!src->data)
+ return 0;
+
if (src->error) {
ret = src->error;
dvb_ringbuffer_flush(src);
@@ -104,6 +110,9 @@
buf += ret;
}
+ if (count - todo) /* some data was read? */
+ wake_up_all(&src->queue);
+
return (count - todo) ? (count - todo) : ret;
}
@@ -126,8 +135,11 @@
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
struct dmx_frontend *front;
+ void *mem;
- dprintk("function : %s\n", __func__);
+ dprintk("function : %s(%X)\n",
+ __func__,
+ (file->f_flags & O_ACCMODE));
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
@@ -145,21 +157,20 @@
}
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- void *mem;
if (!dvbdev->readers) {
mutex_unlock(&dmxdev->mutex);
return -EBUSY;
}
- mem = vmalloc(DVR_BUFFER_SIZE);
+ mem = vmalloc_user(DVR_BUFFER_SIZE);
if (!mem) {
mutex_unlock(&dmxdev->mutex);
return -ENOMEM;
}
dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
dvbdev->readers--;
- }
-
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ } else if (!dvbdev->writers) {
+ dmxdev->dvr_in_exit = 0;
+ dmxdev->dvr_processing_input = 0;
dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
if (!dmxdev->demux->write) {
@@ -173,9 +184,22 @@
mutex_unlock(&dmxdev->mutex);
return -EINVAL;
}
+
+ mem = vmalloc_user(DVR_BUFFER_SIZE);
+ if (!mem) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+
dmxdev->demux->disconnect_frontend(dmxdev->demux);
dmxdev->demux->connect_frontend(dmxdev->demux, front);
+
+ dvb_ringbuffer_init(&dmxdev->dvr_input_buffer,
+ mem,
+ DVR_BUFFER_SIZE);
+ dvbdev->writers--;
}
+
dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
return 0;
@@ -188,11 +212,6 @@
mutex_lock(&dmxdev->mutex);
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
- dmxdev->demux->disconnect_frontend(dmxdev->demux);
- dmxdev->demux->connect_frontend(dmxdev->demux,
- dmxdev->dvr_orig_fe);
- }
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
dvbdev->readers++;
if (dmxdev->dvr_buffer.data) {
@@ -201,6 +220,51 @@
spin_lock_irq(&dmxdev->lock);
dmxdev->dvr_buffer.data = NULL;
spin_unlock_irq(&dmxdev->lock);
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+ vfree(mem);
+ }
+ } else {
+ int i;
+
+ dmxdev->dvr_in_exit = 1;
+ wake_up_all(&dmxdev->dvr_input_buffer.queue);
+
+ /*
+ * There might be dmx filters reading now from DVR
+ * device, in PULL mode, they might be also stalled
+ * on output, signal to them that DVR is exiting.
+ */
+ if (dmxdev->playback_mode == DMX_PB_MODE_PULL) {
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state == DMXDEV_STATE_GO)
+ wake_up_all(
+ &dmxdev->filter[i].buffer.queue);
+ }
+
+ /* notify kernel demux that we are canceling */
+ if (dmxdev->demux->write_cancel)
+ dmxdev->demux->write_cancel(dmxdev->demux);
+
+ /*
+ * Now flush dvr-in workqueue so that no one
+ * would process data from dvr input buffer any more
+ * before it gets freed.
+ */
+ flush_workqueue(dmxdev->dvr_input_workqueue);
+
+ dvbdev->writers++;
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux,
+ dmxdev->dvr_orig_fe);
+
+ if (dmxdev->dvr_input_buffer.data) {
+ void *mem = dmxdev->dvr_input_buffer.data;
+ mb();
+ spin_lock_irq(&dmxdev->dvr_in_lock);
+ dmxdev->dvr_input_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->dvr_in_lock);
vfree(mem);
}
}
@@ -217,17 +281,20 @@
return 0;
}
-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+
+static int dvb_dvr_mmap(struct file *filp, struct vm_area_struct *vma)
{
- struct dvb_device *dvbdev = file->private_data;
+ struct dvb_device *dvbdev = filp->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
+ struct dvb_ringbuffer *buffer;
+ int vma_size;
+ int buffer_size;
int ret;
- if (!dmxdev->demux->write)
- return -EOPNOTSUPP;
- if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+ if (((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+ (vma->vm_flags & VM_WRITE))
return -EINVAL;
+
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
@@ -235,11 +302,114 @@
mutex_unlock(&dmxdev->mutex);
return -ENODEV;
}
- ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+
+ if ((filp->f_flags & O_ACCMODE) == O_RDONLY)
+ buffer = &dmxdev->dvr_buffer;
+ else
+ buffer = &dmxdev->dvr_input_buffer;
+
+ vma_size = vma->vm_end - vma->vm_start;
+
+ /* Make sure requested mapping is not larger than buffer size */
+ buffer_size = buffer->size + (PAGE_SIZE-1);
+ buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+ if (vma_size != buffer_size) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buffer->data, 0);
+ if (ret) {
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_flags |= VM_DONTEXPAND;
+
mutex_unlock(&dmxdev->mutex);
return ret;
}
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ int ret;
+ size_t todo;
+ ssize_t free_space;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ return -EINVAL;
+
+ if ((file->f_flags & O_NONBLOCK) &&
+ (dvb_ringbuffer_free(src) == 0))
+ return -EWOULDBLOCK;
+
+ ret = 0;
+ for (todo = count; todo > 0; todo -= ret) {
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_free(src)) ||
+ (src->error != 0) ||
+ (dmxdev->dvr_in_exit));
+
+ if (ret < 0)
+ return ret;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (!src->data) {
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+ }
+
+ if (dmxdev->exit || dmxdev->dvr_in_exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ mutex_unlock(&dmxdev->mutex);
+ wake_up_all(&src->queue);
+ return ret;
+ }
+
+ free_space = dvb_ringbuffer_free(src);
+
+ if (free_space > todo)
+ free_space = todo;
+
+ ret = dvb_ringbuffer_write_user(src, buf, free_space);
+
+ if (ret < 0) {
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ buf += ret;
+
+ mutex_unlock(&dmxdev->mutex);
+
+ wake_up_all(&src->queue);
+
+ if (!work_pending(&dmxdev->dvr_input_work))
+ queue_work(dmxdev->dvr_input_workqueue,
+ &dmxdev->dvr_input_work);
+ }
+
+ return (count - todo) ? (count - todo) : ret;
+}
+
static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
@@ -254,39 +424,223 @@
buf, count, ppos);
}
-static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
- unsigned long size)
+static void dvr_input_work_func(struct work_struct *worker)
{
- struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+ struct dmxdev *dmxdev =
+ container_of(worker, struct dmxdev, dvr_input_work);
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ int ret;
+ size_t todo;
+ size_t split;
+
+ while (1) {
+ /* wait for input */
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_avail(src)) ||
+ (src->error != 0) ||
+ (dmxdev->dvr_in_exit));
+
+ if (ret < 0)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ break;
+ }
+
+ if (src->error) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ wake_up_all(&src->queue);
+ break;
+ }
+
+ dmxdev->dvr_processing_input = 1;
+
+ ret = dvb_ringbuffer_avail(src);
+ todo = ret;
+
+ split = (src->pread + ret > src->size) ?
+ src->size - src->pread :
+ 0;
+
+ /*
+ * In DVR PULL mode, write might block.
+ * Lock on DVR buffer is released before calling to
+ * write, if DVR was released meanwhile, dvr_in_exit is
+ * prompted. Lock is aquired when updating the read pointer
+ * again to preserve read/write pointers consistancy
+ */
+ if (split > 0) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ dmxdev->demux->write(dmxdev->demux,
+ src->data + src->pread,
+ split);
+
+ if (dmxdev->dvr_in_exit)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ todo -= split;
+ DVB_RINGBUFFER_SKIP(src, split);
+ }
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+ dmxdev->demux->write(dmxdev->demux,
+ src->data + src->pread, todo);
+
+ if (dmxdev->dvr_in_exit)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ DVB_RINGBUFFER_SKIP(src, todo);
+ dmxdev->dvr_processing_input = 0;
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&src->queue);
+ }
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf;
void *newmem;
void *oldmem;
+ spinlock_t *lock;
dprintk("function : %s\n", __func__);
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ }
+
if (buf->size == size)
return 0;
if (!size)
return -EINVAL;
- newmem = vmalloc(size);
+ newmem = vmalloc_user(size);
if (!newmem)
return -ENOMEM;
oldmem = buf->data;
- spin_lock_irq(&dmxdev->lock);
+ spin_lock_irq(lock);
+
+ if (((f_flags & O_ACCMODE) != O_RDONLY) &&
+ (dmxdev->dvr_processing_input)) {
+ spin_unlock_irq(lock);
+ vfree(oldmem);
+ return -EBUSY;
+ }
+
buf->data = newmem;
buf->size = size;
/* reset and not flush in case the buffer shrinks */
dvb_ringbuffer_reset(buf);
- spin_unlock_irq(&dmxdev->lock);
+
+ spin_unlock_irq(lock);
vfree(oldmem);
return 0;
}
+static int dvb_dvr_get_buffer_status(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_ringbuffer *buf;
+ spinlock_t *lock;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ }
+
+ spin_lock_irq(lock);
+
+ dmx_buffer_status->error = buf->error;
+ if (buf->error)
+ dvb_ringbuffer_flush(buf);
+
+ dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+ dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+ dmx_buffer_status->read_offset = buf->pread;
+ dmx_buffer_status->write_offset = buf->pwrite;
+ dmx_buffer_status->size = buf->size;
+
+ spin_unlock_irq(lock);
+
+ return 0;
+}
+
+static int dvb_dvr_release_data(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ u32 bytes_count)
+{
+ ssize_t buff_fullness;
+
+ if (!(f_flags & O_ACCMODE) == O_RDONLY)
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ buff_fullness = dvb_ringbuffer_avail(&dmxdev->dvr_buffer);
+
+ if (bytes_count > buff_fullness)
+ return -EINVAL;
+
+ DVB_RINGBUFFER_SKIP(&dmxdev->dvr_buffer, bytes_count);
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+ return 0;
+}
+
+static int dvb_dvr_feed_data(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ u32 bytes_count)
+{
+ ssize_t free_space;
+ struct dvb_ringbuffer *buffer = &dmxdev->dvr_input_buffer;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY)
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ free_space = dvb_ringbuffer_free(buffer);
+
+ if (bytes_count > free_space)
+ return -EINVAL;
+
+ buffer->pwrite =
+ (buffer->pwrite + bytes_count) % buffer->size;
+
+ wake_up_all(&buffer->queue);
+
+ if (!work_pending(&dmxdev->dvr_input_work))
+ queue_work(dmxdev->dvr_input_workqueue,
+ &dmxdev->dvr_input_work);
+
+ return 0;
+}
+
static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
*dmxdevfilter, int state)
{
@@ -309,7 +663,7 @@
if (dmxdevfilter->state >= DMXDEV_STATE_GO)
return -EBUSY;
- newmem = vmalloc(size);
+ newmem = vmalloc_user(size);
if (!newmem)
return -ENOMEM;
@@ -324,6 +678,212 @@
spin_unlock_irq(&dmxdevfilter->dev->lock);
vfree(oldmem);
+ return 0;
+}
+
+static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
+ unsigned long size)
+{
+ if (dmxdevfilter->pes_buffer_size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ dmxdevfilter->pes_buffer_size = size;
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_source(struct dmxdev_filter *dmxdevfilter,
+ dmx_source_t *source)
+{
+ struct dmxdev *dev;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ dev = dmxdevfilter->dev;
+
+ dev->source = *source;
+
+ if (dev->demux->set_source)
+ return dev->demux->set_source(dev->demux, source);
+
+ return 0;
+}
+
+static int dvb_dmxdev_ts_fullness_callback(
+ struct dmx_ts_feed *filter,
+ int required_space)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ struct dvb_ringbuffer *src;
+ int ret;
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+ || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ src = &dmxdevfilter->buffer;
+ else
+ src = &dmxdevfilter->dev->dvr_buffer;
+
+ do {
+ ret = 0;
+
+ if (dmxdevfilter->dev->dvr_in_exit)
+ return -ENODEV;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if ((!src->data) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO))
+ ret = -EINVAL;
+ else if (src->error)
+ ret = src->error;
+
+ if (ret) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return ret;
+ }
+
+ if (required_space <= dvb_ringbuffer_free(src)) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_free(src) >= required_space) ||
+ (src->error != 0) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO) ||
+ dmxdevfilter->dev->dvr_in_exit);
+
+ if (ret < 0)
+ return ret;
+ } while (1);
+}
+
+static int dvb_dmxdev_sec_fullness_callback(
+ struct dmx_section_filter *filter,
+ int required_space)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ struct dvb_ringbuffer *src = &dmxdevfilter->buffer;
+ int ret;
+
+ do {
+ ret = 0;
+
+ if (dmxdevfilter->dev->dvr_in_exit)
+ return -ENODEV;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if ((!src->data) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO))
+ ret = -EINVAL;
+ else if (src->error)
+ ret = src->error;
+
+ if (ret) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return ret;
+ }
+
+ if (required_space <= dvb_ringbuffer_free(src)) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_free(src) >= required_space) ||
+ (src->error != 0) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO) ||
+ dmxdevfilter->dev->dvr_in_exit);
+
+ if (ret < 0)
+ return ret;
+ } while (1);
+}
+
+static int dvb_dmxdev_set_playback_mode(struct dmxdev_filter *dmxdevfilter,
+ enum dmx_playback_mode_t playback_mode)
+{
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+ if ((playback_mode != DMX_PB_MODE_PUSH) &&
+ (playback_mode != DMX_PB_MODE_PULL))
+ return -EINVAL;
+
+ if (((dmxdev->source < DMX_SOURCE_DVR0) ||
+ !dmxdev->demux->set_playback_mode ||
+ !(dmxdev->capabilities & DMXDEV_CAP_PULL_MODE)) &&
+ (playback_mode == DMX_PB_MODE_PULL))
+ return -EPERM;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ dmxdev->playback_mode = playback_mode;
+
+ return dmxdev->demux->set_playback_mode(
+ dmxdev->demux,
+ dmxdev->playback_mode,
+ dvb_dmxdev_ts_fullness_callback,
+ dvb_dmxdev_sec_fullness_callback);
+}
+
+static int dvb_dmxdev_get_buffer_status(
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+
+ if (!buf->data)
+ return -EINVAL;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+
+ dmx_buffer_status->error = buf->error;
+ if (buf->error)
+ dvb_ringbuffer_flush(buf);
+
+ dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+ dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+ dmx_buffer_status->read_offset = buf->pread;
+ dmx_buffer_status->write_offset = buf->pwrite;
+ dmx_buffer_status->size = buf->size;
+
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ return 0;
+}
+
+static int dvb_dmxdev_release_data(struct dmxdev_filter *dmxdevfilter,
+ u32 bytes_count)
+{
+ ssize_t buff_fullness;
+
+ if (!dmxdevfilter->buffer.data)
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ buff_fullness = dvb_ringbuffer_avail(&dmxdevfilter->buffer);
+
+ if (bytes_count > buff_fullness)
+ return -EINVAL;
+
+ DVB_RINGBUFFER_SKIP(&dmxdevfilter->buffer, bytes_count);
+
+ wake_up_all(&dmxdevfilter->buffer.queue);
return 0;
}
@@ -336,7 +896,7 @@
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
spin_unlock_irq(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
}
static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
@@ -362,7 +922,7 @@
int ret;
if (dmxdevfilter->buffer.error) {
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
return 0;
}
spin_lock(&dmxdevfilter->dev->lock);
@@ -387,7 +947,7 @@
if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
dmxdevfilter->state = DMXDEV_STATE_DONE;
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
return 0;
}
@@ -402,18 +962,34 @@
spin_lock(&dmxdevfilter->dev->lock);
if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
- spin_unlock(&dmxdevfilter->dev->lock);
- return 0;
- }
-
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+ if ((dmxdevfilter->dev->capabilities &
+ DMXDEV_CAP_PCR_EXTRACTION) &&
+ ((dmxdevfilter->params.pes.pes_type == DMX_PES_PCR0) ||
+ (dmxdevfilter->params.pes.pes_type == DMX_PES_PCR1) ||
+ (dmxdevfilter->params.pes.pes_type == DMX_PES_PCR2) ||
+ (dmxdevfilter->params.pes.pes_type == DMX_PES_PCR3))) {
+ /*
+ * Support for reporting PCR and STC pairs to user.
+ * Reported data should have the following format:
+ * <8 bit flags><64 bits of STC> <64bits of PCR>
+ * STC and PCR values are in 27MHz.
+ * The current flags that are defined:
+ * 0x00000001: discontinuity_indicator
+ */
+ buffer = &dmxdevfilter->buffer;
+ } else {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+ } else if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
buffer = &dmxdevfilter->buffer;
else
buffer = &dmxdevfilter->dev->dvr_buffer;
+
if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
+ wake_up_all(&buffer->queue);
return 0;
}
ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
@@ -424,7 +1000,7 @@
buffer->error = ret;
}
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
+ wake_up_all(&buffer->queue);
return 0;
}
@@ -534,6 +1110,8 @@
}
dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+
return 0;
}
@@ -600,7 +1178,9 @@
tsfeed = feed->ts;
tsfeed->priv = filter;
- ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ ret = tsfeed->set(tsfeed, feed->pid,
+ ts_type, ts_pes,
+ filter->pes_buffer_size, timeout);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
return ret;
@@ -629,7 +1209,7 @@
dvb_dmxdev_filter_stop(filter);
if (!filter->buffer.data) {
- mem = vmalloc(filter->buffer.size);
+ mem = vmalloc_user(filter->buffer.size);
if (!mem)
return -ENOMEM;
spin_lock_irq(&filter->dev->lock);
@@ -649,7 +1229,6 @@
*secfilter = NULL;
*secfeed = NULL;
-
/* find active filter/feed with same PID */
for (i = 0; i < dmxdev->filternum; i++) {
if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
@@ -763,6 +1342,8 @@
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
init_timer(&dmxdevfilter->timer);
+ dmxdevfilter->pes_buffer_size = 32768;
+
dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
@@ -788,7 +1369,7 @@
}
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
mutex_unlock(&dmxdevfilter->mutex);
mutex_unlock(&dmxdev->mutex);
return 0;
@@ -1023,6 +1604,24 @@
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_GET_BUFFER_STATUS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_buffer_status(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_RELEASE_DATA:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_release_data(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
case DMX_GET_PES_PIDS:
if (!dmxdev->demux->get_pes_pids) {
ret = -EINVAL;
@@ -1040,11 +1639,59 @@
break;
case DMX_SET_SOURCE:
- if (!dmxdev->demux->set_source) {
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_source(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_TS_PACKET_FORMAT:
+ if (!dmxdev->demux->set_tsp_format) {
ret = -EINVAL;
break;
}
- ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+ ret = -EBUSY;
+ break;
+ }
+ ret = dmxdev->demux->set_tsp_format(
+ dmxdev->demux,
+ *(enum dmx_tsp_format_t *)parg);
+ break;
+
+ case DMX_SET_TS_OUT_FORMAT:
+ if (!dmxdev->demux->set_tsp_out_format) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+ ret = -EBUSY;
+ break;
+ }
+
+ ret = dmxdev->demux->set_tsp_out_format(
+ dmxdev->demux,
+ *(enum dmx_tsp_format_t *)parg);
+ break;
+
+ case DMX_SET_DECODER_BUFFER_SIZE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ ret = dvb_dmxdev_set_pes_buffer_size(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PLAYBACK_MODE:
+ ret = dvb_dmxdev_set_playback_mode(
+ dmxdevfilter,
+ *(enum dmx_playback_mode_t *)parg);
break;
case DMX_GET_STC:
@@ -1114,6 +1761,59 @@
return mask;
}
+static int dvb_demux_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct dmxdev_filter *dmxdevfilter = filp->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ int ret;
+ int vma_size;
+ int buffer_size;
+
+ vma_size = vma->vm_end - vma->vm_start;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ if (!dmxdevfilter->buffer.data) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ /* Make sure requested mapping is not larger than buffer size */
+ buffer_size = dmxdevfilter->buffer.size + (PAGE_SIZE-1);
+ buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+ if (vma_size != buffer_size) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, dmxdevfilter->buffer.data, 0);
+ if (ret) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_flags |= VM_DONTEXPAND;
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+
+ return 0;
+}
+
static int dvb_demux_release(struct inode *inode, struct file *file)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
@@ -1144,6 +1844,7 @@
.release = dvb_demux_release,
.poll = dvb_demux_poll,
.llseek = default_llseek,
+ .mmap = dvb_demux_mmap,
};
static struct dvb_device dvbdev_demux = {
@@ -1166,7 +1867,19 @@
switch (cmd) {
case DMX_SET_BUFFER_SIZE:
- ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+ ret = dvb_dvr_set_buffer_size(dmxdev, file->f_flags, arg);
+ break;
+
+ case DMX_GET_BUFFER_STATUS:
+ ret = dvb_dvr_get_buffer_status(dmxdev, file->f_flags, parg);
+ break;
+
+ case DMX_RELEASE_DATA:
+ ret = dvb_dvr_release_data(dmxdev, file->f_flags, arg);
+ break;
+
+ case DMX_FEED_DATA:
+ ret = dvb_dvr_feed_data(dmxdev, file->f_flags, arg);
break;
default:
@@ -1191,16 +1904,22 @@
dprintk("function : %s\n", __func__);
- poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
-
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
if (dmxdev->dvr_buffer.error)
mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
mask |= (POLLIN | POLLRDNORM | POLLPRI);
- } else
- mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+ } else {
+ poll_wait(file, &dmxdev->dvr_input_buffer.queue, wait);
+ if (dmxdev->dvr_input_buffer.error)
+ mask |= (POLLOUT | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dvb_ringbuffer_free(&dmxdev->dvr_input_buffer))
+ mask |= (POLLOUT | POLLRDNORM | POLLPRI);
+ }
return mask;
}
@@ -1209,6 +1928,7 @@
.owner = THIS_MODULE,
.read = dvb_dvr_read,
.write = dvb_dvr_write,
+ .mmap = dvb_dvr_mmap,
.unlocked_ioctl = dvb_dvr_ioctl,
.open = dvb_dvr_open,
.release = dvb_dvr_release,
@@ -1234,8 +1954,19 @@
if (!dmxdev->filter)
return -ENOMEM;
+ dmxdev->dvr_input_workqueue =
+ create_singlethread_workqueue("dvr_workqueue");
+
+ if (dmxdev->dvr_input_workqueue == NULL) {
+ vfree(dmxdev->filter);
+ return -ENOMEM;
+ }
+
+ dmxdev->playback_mode = DMX_PB_MODE_PUSH;
+
mutex_init(&dmxdev->mutex);
spin_lock_init(&dmxdev->lock);
+ spin_lock_init(&dmxdev->dvr_in_lock);
for (i = 0; i < dmxdev->filternum; i++) {
dmxdev->filter[i].dev = dmxdev;
dmxdev->filter[i].buffer.data = NULL;
@@ -1249,6 +1980,10 @@
dmxdev, DVB_DEVICE_DVR);
dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+ dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
+
+ INIT_WORK(&dmxdev->dvr_input_work,
+ dvr_input_work_func);
return 0;
}
@@ -1267,6 +2002,9 @@
dmxdev->dvr_dvbdev->users==1);
}
+ flush_workqueue(dmxdev->dvr_input_workqueue);
+ destroy_workqueue(dmxdev->dvr_input_workqueue);
+
dvb_unregister_device(dmxdev->dvbdev);
dvb_unregister_device(dmxdev->dvr_dvbdev);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 02ebe28..82f8f6d 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -4,6 +4,8 @@
* Copyright (C) 2000 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -32,7 +34,7 @@
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-
+#include <linux/workqueue.h>
#include <linux/dvb/dmx.h>
#include "dvbdev.h"
@@ -83,14 +85,18 @@
struct mutex mutex;
+ /* relevent for decoder PES */
+ unsigned long pes_buffer_size;
+
/* only for sections */
struct timer_list timer;
int todo;
u8 secheader[3];
};
-
struct dmxdev {
+ struct work_struct dvr_input_work;
+
struct dvb_device *dvbdev;
struct dvb_device *dvr_dvbdev;
@@ -99,16 +105,28 @@
int filternum;
int capabilities;
+#define DMXDEV_CAP_DUPLEX 0x1
+#define DMXDEV_CAP_PULL_MODE 0x2
+#define DMXDEV_CAP_PCR_EXTRACTION 0x4
+
+ enum dmx_playback_mode_t playback_mode;
+ dmx_source_t source;
unsigned int exit:1;
-#define DMXDEV_CAP_DUPLEX 1
+ unsigned int dvr_in_exit:1;
+ unsigned int dvr_processing_input:1;
+
struct dmx_frontend *dvr_orig_fe;
struct dvb_ringbuffer dvr_buffer;
+ struct dvb_ringbuffer dvr_input_buffer;
+ struct workqueue_struct *dvr_input_workqueue;
+
#define DVR_BUFFER_SIZE (10*188*1024)
struct mutex mutex;
spinlock_t lock;
+ spinlock_t dvr_in_lock;
};
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index faa3671..966b48d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -5,6 +5,8 @@
* & Marcus Metzler <marcus@convergence.de>
* for convergence integrated media GmbH
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -50,6 +52,14 @@
MODULE_PARM_DESC(dvb_demux_speedcheck,
"enable transport stream speed check");
+/* counter advancing for each new dvb-demux device */
+static int dvb_demux_index;
+
+static int dvb_demux_performancecheck;
+module_param(dvb_demux_performancecheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_performancecheck,
+ "enable transport stream performance check, reported through debugfs");
+
#define dprintk_tscheck(x...) do { \
if (dvb_demux_tscheck && printk_ratelimit()) \
printk(x); \
@@ -95,6 +105,19 @@
memcpy(d, s, len);
}
+static u32 dvb_dmx_calc_time_delta(struct timespec past_time)
+{
+ struct timespec curr_time, delta_time;
+ u64 delta_time_us;
+
+ curr_time = current_kernel_time();
+ delta_time = timespec_sub(curr_time, past_time);
+ delta_time_us = ((s64)delta_time.tv_sec * USEC_PER_SEC) +
+ delta_time.tv_nsec / 1000;
+
+ return (u32)delta_time_us;
+}
+
/******************************************************************************
* Software filter functions
******************************************************************************/
@@ -120,8 +143,14 @@
printk("missed packet!\n");
*/
- if (buf[1] & 0x40) // PUSI ?
+ /* PUSI ? */
+ if (buf[1] & 0x40) {
feed->peslen = 0xfffa;
+ feed->pusi_seen = 1;
+ }
+
+ if (feed->pusi_seen == 0)
+ return 0;
feed->peslen += count;
@@ -164,10 +193,23 @@
return 0;
if (sec->check_crc) {
+ struct timespec pre_crc_time;
+
+ if (dvb_demux_performancecheck)
+ pre_crc_time = current_kernel_time();
+
section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
if (section_syntax_indicator &&
- demux->check_crc32(feed, sec->secbuf, sec->seclen))
+ demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
+ if (dvb_demux_performancecheck)
+ demux->total_crc_time +=
+ dvb_dmx_calc_time_delta(pre_crc_time);
return -1;
+ }
+
+ if (dvb_demux_performancecheck)
+ demux->total_crc_time +=
+ dvb_dmx_calc_time_delta(pre_crc_time);
}
do {
@@ -351,6 +393,161 @@
return 0;
}
+static inline void dvb_dmx_swfilter_output_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ u8 time_stamp[4] = {0};
+ struct dvb_demux *demux = feed->demux;
+
+ /*
+ * if we output 192 packet with timestamp at head of packet,
+ * output the timestamp now before the 188 TS packet
+ */
+ if (demux->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+ feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+
+ /*
+ * if we output 192 packet with timestamp at tail of packet,
+ * output the timestamp now after the 188 TS packet
+ */
+ if (demux->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+ feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+static inline void dvb_dmx_configure_decoder_fullness(
+ struct dvb_demux *demux,
+ int initialize)
+{
+ struct dvb_demux_feed *feed;
+ int j;
+
+ for (j = 0; j < demux->feednum; j++) {
+ feed = &demux->feed[j];
+
+ if ((feed->state != DMX_STATE_GO) ||
+ (feed->type != DMX_TYPE_TS) ||
+ !(feed->ts_type & TS_DECODER))
+ continue;
+
+ if (initialize) {
+ if (demux->decoder_fullness_init)
+ demux->decoder_fullness_init(feed);
+ } else {
+ if (demux->decoder_fullness_abort)
+ demux->decoder_fullness_abort(feed);
+ }
+ }
+}
+
+static inline int dvb_dmx_swfilter_buffer_check(
+ struct dvb_demux *demux,
+ u16 pid)
+{
+ int desired_space;
+ int ret;
+ struct dmx_ts_feed *ts;
+ struct dvb_demux_filter *f;
+ struct dvb_demux_feed *feed;
+ int was_locked;
+ int i, j;
+
+ if (likely(spin_is_locked(&demux->lock)))
+ was_locked = 1;
+ else
+ was_locked = 0;
+
+ /*
+ * Check that there's enough free space for data output.
+ * If there no space, wait for it (block).
+ * Since this function is called while spinlock
+ * is aquired, the lock should be released first.
+ * Once we get control back, lock is aquired back
+ * and checks that the filter is still valid.
+ */
+ for (j = 0; j < demux->feednum; j++) {
+ feed = &demux->feed[j];
+
+ if (demux->sw_filter_abort)
+ return -ENODEV;
+
+ if ((feed->state != DMX_STATE_GO) ||
+ ((feed->pid != pid) && (feed->pid != 0x2000)))
+ continue;
+
+ if (feed->type == DMX_TYPE_TS) {
+ desired_space = 192; /* upper bound */
+ ts = &feed->feed.ts;
+
+ if (feed->ts_type & TS_PACKET) {
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->buffer_ctrl.ts(ts, desired_space);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ continue;
+ }
+
+ if (demux->sw_filter_abort)
+ return -ENODEV;
+
+ if (!ts->is_filtering)
+ continue;
+
+ if ((feed->ts_type & TS_DECODER) &&
+ (demux->decoder_fullness_wait)) {
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->decoder_fullness_wait(
+ feed,
+ desired_space);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ continue;
+ }
+
+ continue;
+ }
+
+ /* else - section case */
+ desired_space = feed->feed.sec.tsfeedp + 188; /* upper bound */
+ for (i = 0; i < demux->filternum; i++) {
+ if (demux->sw_filter_abort)
+ return -EPERM;
+
+ if (!feed->feed.sec.is_filtering)
+ continue;
+
+ f = &demux->filter[i];
+ if (f->feed != feed)
+ continue;
+
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->buffer_ctrl.sec(&f->filter, desired_space);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return 0;
+}
+
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
const u8 *buf)
{
@@ -362,8 +559,7 @@
if (feed->ts_type & TS_PAYLOAD_ONLY)
dvb_dmx_swfilter_payload(feed, buf);
else
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
- DMX_OK);
+ dvb_dmx_swfilter_output_packet(feed, buf);
}
if (feed->ts_type & TS_DECODER)
if (feed->demux->write_to_decoder)
@@ -446,6 +642,10 @@
/* end check */
};
+ if (demux->playback_mode == DMX_PB_MODE_PULL)
+ if (dvb_dmx_swfilter_buffer_check(demux, pid) < 0)
+ return;
+
list_for_each_entry(feed, &demux->feed_list, list_head) {
if ((feed->pid != pid) && (feed->pid != 0x2000))
continue;
@@ -457,16 +657,25 @@
if (feed->pid == pid)
dvb_dmx_swfilter_packet_type(feed, buf);
- else if (feed->pid == 0x2000)
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+ else if ((feed->pid == 0x2000) &&
+ (feed->feed.ts.is_filtering))
+ dvb_dmx_swfilter_output_packet(feed, buf);
}
}
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
size_t count)
{
+ struct timespec pre_time;
+
+ if (dvb_demux_performancecheck)
+ pre_time = current_kernel_time();
+
spin_lock(&demux->lock);
+ demux->sw_filter_abort = 0;
+ dvb_dmx_configure_decoder_fullness(demux, 1);
+
while (count--) {
if (buf[0] == 0x47)
dvb_dmx_swfilter_packet(demux, buf);
@@ -474,18 +683,24 @@
}
spin_unlock(&demux->lock);
+
+ if (dvb_demux_performancecheck)
+ demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
static inline int find_next_packet(const u8 *buf, int pos, size_t count,
- const int pktsize)
+ const int pktsize, const int leadingbytes)
{
int start = pos, lost;
while (pos < count) {
- if (buf[pos] == 0x47 ||
- (pktsize == 204 && buf[pos] == 0xB8))
+ if ((buf[pos] == 0x47 && !leadingbytes) ||
+ (pktsize == 204 && buf[pos] == 0xB8) ||
+ (pktsize == 192 && leadingbytes &&
+ (pos+leadingbytes < count) &&
+ buf[pos+leadingbytes] == 0x47))
break;
pos++;
}
@@ -495,7 +710,9 @@
/* This garbage is part of a valid packet? */
int backtrack = pos - pktsize;
if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
- (pktsize == 204 && buf[backtrack] == 0xB8)))
+ (pktsize == 204 && buf[backtrack] == 0xB8) ||
+ (pktsize == 192 &&
+ buf[backtrack+leadingbytes] == 0x47)))
return backtrack;
}
@@ -504,13 +721,20 @@
/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
- size_t count, const int pktsize)
+ size_t count, const int pktsize, const int leadingbytes)
{
int p = 0, i, j;
const u8 *q;
+ struct timespec pre_time;
+
+ if (dvb_demux_performancecheck)
+ pre_time = current_kernel_time();
spin_lock(&demux->lock);
+ demux->sw_filter_abort = 0;
+ dvb_dmx_configure_decoder_fullness(demux, 1);
+
if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
i = demux->tsbufp;
j = pktsize - i;
@@ -520,14 +744,22 @@
goto bailout;
}
memcpy(&demux->tsbuf[i], buf, j);
- if (demux->tsbuf[0] == 0x47) /* double check */
+ if (pktsize == 192 &&
+ leadingbytes &&
+ demux->tsbuf[leadingbytes] == 0x47) /* double check */
+ dvb_dmx_swfilter_packet(demux, demux->tsbuf+4);
+ else if (demux->tsbuf[0] == 0x47) /* double check */
dvb_dmx_swfilter_packet(demux, demux->tsbuf);
demux->tsbufp = 0;
p += j;
}
while (1) {
- p = find_next_packet(buf, p, count, pktsize);
+ p = find_next_packet(buf, p, count, pktsize, leadingbytes);
+
+ if (demux->sw_filter_abort)
+ goto bailout;
+
if (p >= count)
break;
if (count - p < pktsize)
@@ -540,6 +772,10 @@
demux->tsbuf[0] = 0x47;
q = demux->tsbuf;
}
+
+ if (pktsize == 192 && leadingbytes)
+ q = &buf[p+leadingbytes];
+
dvb_dmx_swfilter_packet(demux, q);
p += pktsize;
}
@@ -554,20 +790,55 @@
bailout:
spin_unlock(&demux->lock);
+
+ if (dvb_demux_performancecheck)
+ demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
}
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- _dvb_dmx_swfilter(demux, buf, count, 188);
+ _dvb_dmx_swfilter(demux, buf, count, 188, 0);
}
EXPORT_SYMBOL(dvb_dmx_swfilter);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- _dvb_dmx_swfilter(demux, buf, count, 204);
+ _dvb_dmx_swfilter(demux, buf, count, 204, 0);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+void dvb_dmx_swfilter_format(
+ struct dvb_demux *demux,
+ const u8 *buf,
+ size_t count,
+ enum dmx_tsp_format_t tsp_format)
+{
+ switch (tsp_format) {
+ case DMX_TSP_FORMAT_188:
+ _dvb_dmx_swfilter(demux, buf, count, 188, 0);
+ break;
+
+ case DMX_TSP_FORMAT_192_TAIL:
+ _dvb_dmx_swfilter(demux, buf, count, 192, 0);
+ break;
+
+ case DMX_TSP_FORMAT_192_HEAD:
+ _dvb_dmx_swfilter(demux, buf, count, 192, 4);
+ break;
+
+ case DMX_TSP_FORMAT_204:
+ _dvb_dmx_swfilter(demux, buf, count, 204, 0);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: invalid TS packet format (format=%d)\n",
+ __func__,
+ tsp_format);
+ break;
+ }
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_format);
+
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
{
int i;
@@ -778,6 +1049,13 @@
feed->peslen = 0xfffa;
feed->buffer = NULL;
+ /* default behaviour - pass first PES data even if it is
+ * partial PES data from previous PES that we didn't receive its header.
+ * Override this to 0 in your start_feed function in order to handle
+ * first PES differently.
+ */
+ feed->pusi_seen = 1;
+
(*ts_feed) = &feed->feed.ts;
(*ts_feed)->parent = dmx;
(*ts_feed)->priv = NULL;
@@ -1119,30 +1397,54 @@
return 0;
}
-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
{
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- void *p;
if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
return -EINVAL;
- p = memdup_user(buf, count);
- if (IS_ERR(p))
- return PTR_ERR(p);
- if (mutex_lock_interruptible(&dvbdemux->mutex)) {
- kfree(p);
- return -ERESTARTSYS;
- }
- dvb_dmx_swfilter(dvbdemux, p, count);
- kfree(p);
- mutex_unlock(&dvbdemux->mutex);
+ dvb_dmx_swfilter_format(dvbdemux, buf, count, dvbdemux->tsp_format);
if (signal_pending(current))
return -EINTR;
return count;
}
+static int dvbdmx_write_cancel(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+ spin_lock_irq(&dvbdmx->lock);
+
+ /* cancel any pending wait for decoder's buffers */
+ dvbdmx->sw_filter_abort = 1;
+ dvbdmx->tsbufp = 0;
+ dvb_dmx_configure_decoder_fullness(dvbdmx, 0);
+
+ spin_unlock_irq(&dvbdmx->lock);
+
+ return 0;
+}
+
+static int dvbdmx_set_playback_mode(struct dmx_demux *demux,
+ enum dmx_playback_mode_t mode,
+ dmx_ts_fullness ts_fullness_callback,
+ dmx_section_fullness sec_fullness_callback)
+{
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ dvbdmx->playback_mode = mode;
+ dvbdmx->buffer_ctrl.ts = ts_fullness_callback;
+ dvbdmx->buffer_ctrl.sec = sec_fullness_callback;
+
+ mutex_unlock(&dvbdmx->mutex);
+
+ return 0;
+}
+
static int dvbdmx_add_frontend(struct dmx_demux *demux,
struct dmx_frontend *frontend)
{
@@ -1214,6 +1516,40 @@
return 0;
}
+static int dvbdmx_set_tsp_format(
+ struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if ((tsp_format > DMX_TSP_FORMAT_204) ||
+ (tsp_format < DMX_TSP_FORMAT_188))
+ return -EINVAL;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ dvbdemux->tsp_format = tsp_format;
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dvbdmx_set_tsp_out_format(
+ struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if ((tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+ (tsp_format < DMX_TSP_FORMAT_188))
+ return -EINVAL;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ dvbdemux->tsp_out_format = tsp_format;
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
int dvb_dmx_init(struct dvb_demux *dvbdemux)
{
int i;
@@ -1232,6 +1568,30 @@
dvbdemux->filter = NULL;
return -ENOMEM;
}
+
+ dvbdemux->total_process_time = 0;
+ dvbdemux->total_crc_time = 0;
+ snprintf(dvbdemux->alias,
+ MAX_DVB_DEMUX_NAME_LEN,
+ "demux%d",
+ dvb_demux_index++);
+
+ dvbdemux->debugfs_demux_dir = debugfs_create_dir(dvbdemux->alias, NULL);
+
+ if (dvbdemux->debugfs_demux_dir != NULL) {
+ debugfs_create_u32(
+ "total_processing_time",
+ S_IRUGO|S_IWUGO,
+ dvbdemux->debugfs_demux_dir,
+ &dvbdemux->total_process_time);
+
+ debugfs_create_u32(
+ "total_crc_time",
+ S_IRUGO|S_IWUGO,
+ dvbdemux->debugfs_demux_dir,
+ &dvbdemux->total_crc_time);
+ }
+
for (i = 0; i < dvbdemux->filternum; i++) {
dvbdemux->filter[i].state = DMX_STATE_FREE;
dvbdemux->filter[i].index = i;
@@ -1258,6 +1618,9 @@
dvbdemux->recording = 0;
dvbdemux->tsbufp = 0;
+ dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
+ dvbdemux->tsp_out_format = DMX_TSP_FORMAT_188;
+
if (!dvbdemux->check_crc32)
dvbdemux->check_crc32 = dvb_dmx_crc32;
@@ -1269,6 +1632,8 @@
dmx->open = dvbdmx_open;
dmx->close = dvbdmx_close;
dmx->write = dvbdmx_write;
+ dmx->write_cancel = dvbdmx_write_cancel;
+ dmx->set_playback_mode = dvbdmx_set_playback_mode;
dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
dmx->release_ts_feed = dvbdmx_release_ts_feed;
dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
@@ -1281,6 +1646,9 @@
dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
dmx->get_pes_pids = dvbdmx_get_pes_pids;
+ dmx->set_tsp_format = dvbdmx_set_tsp_format;
+ dmx->set_tsp_out_format = dvbdmx_set_tsp_out_format;
+
mutex_init(&dvbdemux->mutex);
spin_lock_init(&dvbdemux->lock);
@@ -1291,6 +1659,9 @@
void dvb_dmx_release(struct dvb_demux *dvbdemux)
{
+ if (dvbdemux->debugfs_demux_dir != NULL)
+ debugfs_remove_recursive(dvbdemux->debugfs_demux_dir);
+
vfree(dvbdemux->cnt_storage);
vfree(dvbdemux->filter);
vfree(dvbdemux->feed);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index a7d876f..297f3df 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -4,6 +4,8 @@
* Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
* for convergence integrated media GmbH
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -27,6 +29,7 @@
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/debugfs.h>
#include "demux.h"
@@ -107,6 +110,10 @@
int (*stop_feed)(struct dvb_demux_feed *feed);
int (*write_to_decoder)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
+ int (*decoder_fullness_init)(struct dvb_demux_feed *feed);
+ int (*decoder_fullness_wait)(struct dvb_demux_feed *feed,
+ size_t required_space);
+ int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
u32 (*check_crc32)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
@@ -136,6 +143,28 @@
struct timespec speed_last_time; /* for TS speed check */
uint32_t speed_pkts_cnt; /* for TS speed check */
+
+ enum dmx_tsp_format_t tsp_format;
+ enum dmx_tsp_format_t tsp_out_format;
+
+ enum dmx_playback_mode_t playback_mode;
+ int sw_filter_abort;
+
+ struct {
+ dmx_ts_fullness ts;
+ dmx_section_fullness sec;
+ } buffer_ctrl;
+
+ /*
+ * the following is used for debugfs exposing info
+ * about dvb demux performance.
+ */
+#define MAX_DVB_DEMUX_NAME_LEN 10
+ char alias[MAX_DVB_DEMUX_NAME_LEN];
+
+ u32 total_process_time;
+ u32 total_crc_time;
+ struct dentry *debugfs_demux_dir;
};
int dvb_dmx_init(struct dvb_demux *dvbdemux);
@@ -145,5 +174,10 @@
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
size_t count);
+void dvb_dmx_swfilter_format(
+ struct dvb_demux *demux, const u8 *buf,
+ size_t count,
+ enum dmx_tsp_format_t tsp_format);
+
#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index a5712cd..36cc475 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -5,6 +5,8 @@
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* based on code originally found in av7110.c & dvb_ci.c:
* Copyright (C) 1999-2003 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
@@ -37,6 +39,8 @@
#define PKT_READY 0
#define PKT_DISPOSED 1
+#define PKT_PENDING 2
+
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -166,6 +170,35 @@
return len;
}
+ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+ const u8 *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ split = (rbuf->pwrite + len > rbuf->size) ?
+ rbuf->size - rbuf->pwrite :
+ 0;
+
+ if (split > 0) {
+ if (copy_from_user(rbuf->data + rbuf->pwrite, buf, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ rbuf->pwrite = 0;
+ }
+
+ if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) {
+ rbuf->pwrite = oldpwrite;
+ return -EFAULT;
+ }
+
+ rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+ return len;
+}
+
ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
{
int status;
@@ -180,6 +213,31 @@
return status;
}
+ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf, size_t len)
+{
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_PENDING);
+
+ return oldpwrite;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_start);
+
+int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx)
+{
+ idx = (idx + 2) % rbuf->size;
+
+ if (rbuf->data[idx] != PKT_PENDING)
+ return -EINVAL;
+
+ rbuf->data[idx] = PKT_READY;
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_close);
+
ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8 __user *buf, size_t len)
{
@@ -187,6 +245,9 @@
size_t split;
size_t pktlen;
+ if (DVB_RINGBUFFER_PEEK(rbuf, (idx+2)) != PKT_READY)
+ return -EINVAL;
+
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
if (offset > pktlen) return -EINVAL;
@@ -207,6 +268,7 @@
return len;
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read_user);
ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8* buf, size_t len)
@@ -215,9 +277,13 @@
size_t split;
size_t pktlen;
+ if (rbuf->data[(idx + 2) % rbuf->size] != PKT_READY)
+ return -EINVAL;
+
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
if (offset > pktlen) return -EINVAL;
+
if ((offset + len) > pktlen) len = pktlen - offset;
idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
@@ -232,6 +298,7 @@
memcpy(buf, rbuf->data+idx, todo);
return len;
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
{
@@ -251,6 +318,7 @@
}
}
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
{
@@ -279,6 +347,9 @@
return idx;
}
+ if (curpktstatus == PKT_PENDING)
+ return -EFAULT;
+
consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
}
@@ -286,8 +357,7 @@
// no packets available
return -1;
}
-
-
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
@@ -297,3 +367,5 @@
EXPORT_SYMBOL(dvb_ringbuffer_read_user);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_write_user);
+
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 41f04da..8b591a6 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -5,6 +5,8 @@
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* based on code originally found in av7110.c & dvb_ci.c:
* Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
@@ -134,6 +136,8 @@
extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
size_t len);
+extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+ const u8 *buf, size_t len);
/**
* Write a packet into the ringbuffer.
@@ -183,4 +187,30 @@
extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+/**
+ * Start a new packet that will be written directly by the user to the packet buffer.
+ * The function only writes the header of the packet into the packet buffer,
+ * and the packet is in pending state (can't be read by the reader) until it is
+ * closed using dvb_ringbuffer_pkt_close. You must write the data into the
+ * packet buffer using dvb_ringbuffer_write followed by
+ * dvb_ringbuffer_pkt_close.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <len> Size of the packet's data
+ * returns Index of the packet's header that was started.
+ */
+extern ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf,
+ size_t len);
+
+/**
+ * Close a packet that was started using dvb_ringbuffer_pkt_start.
+ * The packet will be marked as ready to be ready.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index that was returned by dvb_ringbuffer_pkt_start
+ * returns error status, -EINVAL if the provided index is invalid
+ */
+extern int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx);
+
+
#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/mpq/Kconfig b/drivers/media/dvb/mpq/Kconfig
new file mode 100644
index 0000000..868ad8c
--- /dev/null
+++ b/drivers/media/dvb/mpq/Kconfig
@@ -0,0 +1,12 @@
+config DVB_MPQ
+ tristate "Qualcomm Multimedia Processor DVB Adapter"
+ depends on ARCH_MSM && DVB_CORE
+ default n
+
+ help
+ Support for Qualcomm MPQ based DVB adapter.
+ Say Y or M if you own such a device and want to use it.
+
+source "drivers/media/dvb/mpq/demux/Kconfig"
+
+
diff --git a/drivers/media/dvb/mpq/Makefile b/drivers/media/dvb/mpq/Makefile
new file mode 100644
index 0000000..7ccf13e
--- /dev/null
+++ b/drivers/media/dvb/mpq/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_DVB_MPQ) += adapter/
+obj-$(CONFIG_DVB_MPQ_DEMUX) += demux/
+
+
diff --git a/drivers/media/dvb/mpq/adapter/Makefile b/drivers/media/dvb/mpq/adapter/Makefile
new file mode 100644
index 0000000..ed664da
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/Makefile
@@ -0,0 +1,8 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o
+
+mpq-adapter-y := mpq_adapter.o mpq_stream_buffer.o
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_adapter.c b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
new file mode 100644
index 0000000..9664f04
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
@@ -0,0 +1,212 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "mpq_adapter.h"
+#include "mpq_dvb_debug.h"
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* data-structure holding MPQ adapter information */
+static struct
+{
+ /* MPQ adapter registered to dvb-core */
+ struct dvb_adapter adapter;
+
+ /* mutex protect against the data-structure */
+ struct mutex mutex;
+
+ /* List of stream interfaces registered to the MPQ adapter */
+ struct {
+ /* pointer to the stream buffer using for data tunneling */
+ struct mpq_streambuffer *stream_buffer;
+
+ /* callback triggered when the stream interface is registered */
+ mpq_adapter_stream_if_callback callback;
+
+ /* parameter passed to the callback function */
+ void *user_param;
+ } interfaces[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+} mpq_info;
+
+
+/**
+ * Initialize MPQ DVB adapter module.
+ *
+ * Return error status
+ */
+static int __init mpq_adapter_init(void)
+{
+ int i;
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ mutex_init(&mpq_info.mutex);
+
+ /* reset stream interfaces list */
+ for (i = 0; i < MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; i++) {
+ mpq_info.interfaces[i].stream_buffer = NULL;
+ mpq_info.interfaces[i].callback = NULL;
+ }
+
+ /* regsiter a new dvb-adapter to dvb-core */
+ result = dvb_register_adapter(&mpq_info.adapter,
+ "Qualcomm DVB adapter",
+ THIS_MODULE,
+ NULL,
+ adapter_nr);
+
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: dvb_register_adapter failed, errno %d\n",
+ __func__,
+ result);
+ }
+
+ return result;
+}
+
+
+/**
+ * Cleanup MPQ DVB adapter module.
+ */
+static void __exit mpq_adapter_exit(void)
+{
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* un-regsiter adapter from dvb-core */
+ dvb_unregister_adapter(&mpq_info.adapter);
+ mutex_destroy(&mpq_info.mutex);
+}
+
+struct dvb_adapter *mpq_adapter_get(void)
+{
+ return &mpq_info.adapter;
+}
+EXPORT_SYMBOL(mpq_adapter_get);
+
+
+int mpq_adapter_register_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer *stream_buffer)
+{
+ int ret;
+
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) {
+ ret = -EINVAL;
+ goto register_failed;
+ }
+
+ if (mutex_lock_interruptible(&mpq_info.mutex)) {
+ ret = -ERESTARTSYS;
+ goto register_failed;
+ }
+
+ if (mpq_info.interfaces[interface_id].stream_buffer != NULL) {
+ /* already registered interface */
+ ret = -EINVAL;
+ goto register_failed_unlock_mutex;
+ }
+
+ mpq_info.interfaces[interface_id].stream_buffer = stream_buffer;
+ mutex_unlock(&mpq_info.mutex);
+
+ /*
+ * If callback is installed, trigger it to notify that
+ * stream interface was registered.
+ */
+ if (mpq_info.interfaces[interface_id].callback != NULL) {
+ mpq_info.interfaces[interface_id].callback(
+ interface_id,
+ mpq_info.interfaces[interface_id].user_param);
+ }
+
+ return 0;
+
+register_failed_unlock_mutex:
+ mutex_unlock(&mpq_info.mutex);
+register_failed:
+ return ret;
+}
+EXPORT_SYMBOL(mpq_adapter_register_stream_if);
+
+
+int mpq_adapter_unregister_stream_if(
+ enum mpq_adapter_stream_if interface_id)
+{
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ /* clear the registered interface */
+ mpq_info.interfaces[interface_id].stream_buffer = NULL;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_unregister_stream_if);
+
+
+int mpq_adapter_get_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer **stream_buffer)
+{
+ if ((interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) ||
+ (stream_buffer == NULL))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ *stream_buffer = mpq_info.interfaces[interface_id].stream_buffer;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_get_stream_if);
+
+
+int mpq_adapter_notify_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ mpq_adapter_stream_if_callback callback,
+ void *user_param)
+{
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ mpq_info.interfaces[interface_id].callback = callback;
+ mpq_info.interfaces[interface_id].user_param = user_param;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_notify_stream_if);
+
+
+module_init(mpq_adapter_init);
+module_exit(mpq_adapter_exit);
+
+MODULE_DESCRIPTION("Qualcomm MPQ adapter");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
new file mode 100644
index 0000000..738d730
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_stream_buffer.h"
+
+
+void mpq_streambuffer_init(
+ struct mpq_streambuffer *sbuff,
+ void *data_buff, size_t data_buff_len,
+ void *packet_buff, size_t packet_buff_size)
+{
+ dvb_ringbuffer_init(&sbuff->raw_data, data_buff, data_buff_len);
+ dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
+}
+EXPORT_SYMBOL(mpq_streambuffer_init);
+
+
+ssize_t mpq_streambuffer_pkt_next(
+ struct mpq_streambuffer *sbuff,
+ ssize_t idx, size_t *pktlen)
+{
+ return dvb_ringbuffer_pkt_next(&sbuff->packet_data, idx, pktlen);
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_next);
+
+
+ssize_t mpq_streambuffer_pkt_read(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data)
+{
+ size_t ret;
+ size_t read_len;
+
+ /* read-out the packet header first */
+ ret = dvb_ringbuffer_pkt_read(
+ &sbuff->packet_data, idx, 0,
+ (u8 *)packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ /* verify length, at least packet header should exist */
+ if (ret != sizeof(struct mpq_streambuffer_packet_header))
+ return -EINVAL;
+
+ read_len = ret;
+
+ /* read-out private user-data if there are such */
+ if ((packet->user_data_len) && (user_data != NULL)) {
+ ret = dvb_ringbuffer_pkt_read(
+ &sbuff->packet_data,
+ idx,
+ sizeof(struct mpq_streambuffer_packet_header),
+ user_data,
+ packet->user_data_len);
+
+ if (ret < 0)
+ return ret;
+
+ read_len += ret;
+ }
+
+ return read_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_read);
+
+
+int mpq_streambuffer_pkt_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ int dispose_data)
+{
+ int ret;
+ struct mpq_streambuffer_packet_header packet;
+
+ if (dispose_data) {
+ /* read-out the packet header first */
+ ret = dvb_ringbuffer_pkt_read(
+ &sbuff->packet_data,
+ idx,
+ 0,
+ (u8 *)&packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ if (ret != sizeof(struct mpq_streambuffer_packet_header))
+ return -EINVAL;
+
+ /* Advance the read pointer in the raw-data buffer first */
+ ret = mpq_streambuffer_data_read_dispose(
+ sbuff,
+ packet.raw_data_len);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Now clear the packet from the packet header */
+ dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
+
+
+int mpq_streambuffer_pkt_write(
+ struct mpq_streambuffer *sbuff,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data)
+{
+ ssize_t idx;
+ size_t len;
+
+ len =
+ sizeof(struct mpq_streambuffer_packet_header) +
+ packet->user_data_len;
+
+ /* Make sure enough space available for packet header */
+ if (dvb_ringbuffer_free(&sbuff->packet_data) < len)
+ return -ENOSPC;
+
+ /* Starting writting packet header */
+ idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);
+
+ /* Write non-user private data header */
+ dvb_ringbuffer_write(
+ &sbuff->packet_data,
+ (u8 *)packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ /* Write user's own private data header */
+ dvb_ringbuffer_write(&sbuff->packet_data,
+ user_data,
+ packet->user_data_len);
+
+ dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);
+
+ wake_up_all(&sbuff->packet_data.queue);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
+
+
+ssize_t mpq_streambuffer_data_write(
+ struct mpq_streambuffer *sbuff,
+ const u8 *buf, size_t len)
+{
+ int res;
+
+ if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+ return -ENOSPC;
+
+ res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
+ wake_up_all(&sbuff->raw_data.queue);
+
+ return res;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write);
+
+
+int mpq_streambuffer_data_write_deposit(
+ struct mpq_streambuffer *sbuff,
+ size_t len)
+{
+ if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+ return -ENOSPC;
+
+ sbuff->raw_data.pwrite =
+ (sbuff->raw_data.pwrite+len) % sbuff->raw_data.size;
+
+ wake_up_all(&sbuff->raw_data.queue);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write_deposit);
+
+
+size_t mpq_streambuffer_data_read(
+ struct mpq_streambuffer *sbuff,
+ u8 *buf, size_t len)
+{
+ int actual_len;
+
+ actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
+ if (actual_len < len)
+ len = actual_len;
+
+ if (actual_len)
+ dvb_ringbuffer_read(&sbuff->raw_data, buf, actual_len);
+
+ wake_up_all(&sbuff->raw_data.queue);
+
+ return actual_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read);
+
+
+int mpq_streambuffer_data_read_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t len)
+{
+ if (unlikely(dvb_ringbuffer_avail(&sbuff->raw_data) < len))
+ return -EINVAL;
+
+ DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
+
+ wake_up_all(&sbuff->raw_data.queue);
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
+
diff --git a/drivers/media/dvb/mpq/demux/Kconfig b/drivers/media/dvb/mpq/demux/Kconfig
new file mode 100644
index 0000000..34961c2
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Kconfig
@@ -0,0 +1,45 @@
+config DVB_MPQ_DEMUX
+ tristate "DVB Demux Device"
+ depends on DVB_MPQ && ION && ION_MSM
+ default n
+
+ help
+ Support for Qualcomm based dvb demux device.
+ Say Y or M if you own such a device and want to use it.
+
+config DVB_MPQ_NUM_DMX_DEVICES
+ int "Number of demux devices"
+ depends on DVB_MPQ_DEMUX
+ default 4
+ range 1 255
+
+ help
+ Configure number of demux devices. Depends on your use-cases for maximum concurrent stream playback.
+
+choice
+ prompt "Demux Hardware Plugin"
+ depends on DVB_MPQ_DEMUX
+ default DVB_MPQ_TSIF
+ help
+ Enable support of specific demux HW plugin depending on the existing HW support.
+ Depending on the enabled HW, demux may take advantage of HW capbailities to perform some tasks in HW instead of SW.
+
+ config DVB_MPQ_TSPP1
+ bool "TSPPv1 plugin"
+ depends on TSPP
+ help
+ Use this option of your HW has Transport Stream Packet Processor (TSPP) version1 support
+
+ config DVB_MPQ_TSPP2
+ bool "TSPPv2 plugin"
+ depends on TSPP
+ help
+ Use this option of your HW has Transport Stream Packet Processor (TSPP) version2 support
+
+ config DVB_MPQ_TSIF
+ bool "TSIF plugin"
+ depends on TSIF
+ help
+ Use this option of your HW has only TSIF input without any Transport Stream Packet Processor (TSPP) support
+endchoice
+
diff --git a/drivers/media/dvb/mpq/demux/Makefile b/drivers/media/dvb/mpq/demux/Makefile
new file mode 100644
index 0000000..b9310c3
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Makefile
@@ -0,0 +1,14 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ_DEMUX) += mpq-dmx-hw-plugin.o
+
+mpq-dmx-hw-plugin-y := mpq_dmx_plugin_common.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP1) += mpq_dmx_plugin_tspp_v1.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP2) += mpq_dmx_plugin_tspp_v2.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSIF) += mpq_dmx_plugin_tsif.o
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
new file mode 100644
index 0000000..e7bbfcb
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -0,0 +1,1239 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+/* Length of mandatory fields that must exist in header of video PES */
+#define PES_MANDATORY_FIELDS_LEN 9
+
+
+/*
+ * 500 PES header packets in the meta-data buffer,
+ * should be more than enough
+ */
+#define VIDEO_NUM_OF_PES_PACKETS 500
+
+#define VIDEO_META_DATA_BUFFER_SIZE \
+ (VIDEO_NUM_OF_PES_PACKETS * \
+ (DVB_RINGBUFFER_PKTHDRSIZE + \
+ sizeof(struct mpq_streambuffer_packet_header) + \
+ sizeof(struct mpq_adapter_video_meta_data)))
+
+/*
+ * The following threshold defines gap from end of ring-buffer
+ * from which new PES payload will not be written to make
+ * sure that the PES payload does not wrap-around at end of the
+ * buffer. Instead, padding will be inserted and the new PES will
+ * be written from the beginning of the buffer.
+ * Setting this to 0 means no padding will be added.
+ */
+#define VIDEO_WRAP_AROUND_THRESHOLD (1024*1024+512*1024)
+
+/*
+ * PCR/STC information length saved in ring-buffer.
+ * PCR / STC are saved in ring-buffer in the following form:
+ * <8 bit flags><64 bits of STC> <64bits of PCR>
+ * STC and PCR values are in 27MHz.
+ * The current flags that are defined:
+ * 0x00000001: discontinuity_indicator
+ */
+#define PCR_STC_LEN 17
+
+
+/* Number of demux devices, has default of linux configuration */
+static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+module_param(mpq_demux_device_num, int, S_IRUGO);
+
+
+/* Global data-structure for managing demux devices */
+static struct
+{
+ /* ION demux client used for memory allocation */
+ struct ion_client *ion_client;
+
+ /* demux devices array */
+ struct mpq_demux *devices;
+
+ /* Stream buffers objects used for tunneling to decoders */
+ struct mpq_streambuffer
+ decoder_buffers[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+
+ /*
+ * Indicates whether we allow decoder's data to
+ * wrap-around in the output buffer or padding is
+ * inserted in such case.
+ */
+ int decoder_data_wrap;
+} mpq_dmx_info;
+
+
+/* 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)
+{
+ /* 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;
+
+ /* stream_id is video? */
+ if ((pes_header->stream_id & 0xF0) != 0xE0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/* Extend dvb-demux debugfs with HW statistics */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
+{
+ /*
+ * Extend dvb-demux debugfs with HW statistics.
+ * Note that destruction of debugfs directory is done
+ * when dvb-demux is terminated.
+ */
+ mpq_demux->hw_notification_count = 0;
+ mpq_demux->hw_notification_rate = 0;
+ mpq_demux->hw_notification_size = 0;
+ mpq_demux->decoder_tsp_drop_count = 0;
+
+ if (mpq_demux->demux.debugfs_demux_dir != NULL) {
+ debugfs_create_u32(
+ "hw_notification_rate",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.debugfs_demux_dir,
+ &mpq_demux->hw_notification_rate);
+
+ debugfs_create_u32(
+ "hw_notification_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.debugfs_demux_dir,
+ &mpq_demux->hw_notification_count);
+
+ debugfs_create_u32(
+ "hw_notification_size",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.debugfs_demux_dir,
+ &mpq_demux->hw_notification_size);
+
+ debugfs_create_u32(
+ "decoder_tsp_drop_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.debugfs_demux_dir,
+ &mpq_demux->decoder_tsp_drop_count);
+ }
+}
+EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
+
+
+/* Update dvb-demux debugfs with HW notification statistics */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
+{
+ struct timespec curr_time, delta_time;
+ u64 delta_time_ms;
+
+ curr_time = current_kernel_time();
+ if (likely(mpq_demux->hw_notification_count)) {
+ /* calculate time-delta between notifications */
+ delta_time =
+ timespec_sub(
+ curr_time,
+ mpq_demux->last_notification_time);
+
+ delta_time_ms = (u64)timespec_to_ns(&delta_time);
+ delta_time_ms = div64_u64(delta_time_ms, 1000000); /* ns->ms */
+
+ mpq_demux->hw_notification_rate = delta_time_ms;
+ }
+
+ mpq_demux->hw_notification_count++;
+ mpq_demux->last_notification_time = curr_time;
+}
+EXPORT_SYMBOL(mpq_dmx_update_hw_statistics);
+
+
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
+{
+ int i;
+ int result;
+ struct mpq_demux *mpq_demux;
+ struct dvb_adapter *mpq_adapter;
+
+ MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
+ __func__,
+ mpq_demux_device_num);
+
+ mpq_adapter = mpq_adapter_get();
+
+ if (mpq_adapter == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_adapter is not valid\n",
+ __func__);
+ result = -EPERM;
+ goto init_failed;
+ }
+
+ if (mpq_demux_device_num == 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_demux_device_num set to 0\n",
+ __func__);
+
+ result = -EPERM;
+ goto init_failed;
+ }
+
+ mpq_dmx_info.devices = NULL;
+ mpq_dmx_info.ion_client = NULL;
+
+ /* TODO: the following should be set based on the decoder */
+ mpq_dmx_info.decoder_data_wrap = 0;
+
+ /* Allocate memory for all MPQ devices */
+ mpq_dmx_info.devices =
+ vmalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
+
+ if (!mpq_dmx_info.devices) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: failed to allocate devices memory\n",
+ __func__);
+
+ result = -ENOMEM;
+ goto init_failed;
+ }
+
+ /* Zero allocated memory */
+ memset(mpq_dmx_info.devices,
+ 0,
+ mpq_demux_device_num*sizeof(struct mpq_demux));
+
+ /*
+ * Create a new ION client used by demux to allocate memory
+ * for decoder's buffers.
+ */
+ mpq_dmx_info.ion_client =
+ msm_ion_client_create(UINT_MAX, "demux client");
+
+ if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_client_create\n",
+ __func__);
+
+ result = PTR_ERR(mpq_dmx_info.ion_client);
+ mpq_dmx_info.ion_client = NULL;
+ goto init_failed_free_demux_devices;
+ }
+
+ /* Initialize and register all demux devices to the system */
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ mpq_demux = mpq_dmx_info.devices+i;
+
+ /* initialize demux source to memory by default */
+ mpq_demux->source = DMX_SOURCE_DVR0 + i;
+
+ /*
+ * Give the plugin pointer to the ion client so
+ * that it can allocate memory from ION if it requires so
+ */
+ mpq_demux->ion_client = mpq_dmx_info.ion_client;
+
+ spin_lock_init(&mpq_demux->feed_lock);
+
+ /*
+ * mpq_demux_plugin_hw_init should be implemented
+ * by the specific plugin
+ */
+ result = dmx_init_func(mpq_adapter, mpq_demux);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: dmx_init_func (errno=%d)\n",
+ __func__,
+ result);
+
+ goto init_failed_free_demux_devices;
+ }
+
+ mpq_demux->is_initialized = 1;
+
+ /*
+ * Add capability of receiving input from memory.
+ * Every demux in our system may be connected to memory input,
+ * or any live input.
+ */
+ mpq_demux->fe_memory.source = DMX_MEMORY_FE;
+ result =
+ mpq_demux->demux.dmx.add_frontend(
+ &mpq_demux->demux.dmx,
+ &mpq_demux->fe_memory);
+
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: add_frontend (mem) failed (errno=%d)\n",
+ __func__,
+ result);
+
+ goto init_failed_free_demux_devices;
+ }
+ }
+
+ return 0;
+
+init_failed_free_demux_devices:
+ mpq_dmx_plugin_exit();
+init_failed:
+ return result;
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_init);
+
+
+void mpq_dmx_plugin_exit(void)
+{
+ int i;
+ struct mpq_demux *mpq_demux;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ if (mpq_dmx_info.ion_client != NULL) {
+ ion_client_destroy(mpq_dmx_info.ion_client);
+ mpq_dmx_info.ion_client = NULL;
+ }
+
+ if (mpq_dmx_info.devices != NULL) {
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ mpq_demux = mpq_dmx_info.devices+i;
+
+ if (mpq_demux->is_initialized) {
+ mpq_demux->demux.dmx.remove_frontend(
+ &mpq_demux->demux.dmx,
+ &mpq_demux->fe_memory);
+
+ dvb_dmxdev_release(&mpq_demux->dmxdev);
+ dvb_dmx_release(&mpq_demux->demux);
+ }
+ }
+
+ vfree(mpq_dmx_info.devices);
+ mpq_dmx_info.devices = NULL;
+ }
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_exit);
+
+
+int mpq_dmx_set_source(
+ struct dmx_demux *demux,
+ const dmx_source_t *src)
+{
+ int i;
+ int dvr_index;
+ int dmx_index;
+ struct mpq_demux *mpq_demux = (struct mpq_demux *)demux->priv;
+
+ if ((mpq_dmx_info.devices == NULL) || (mpq_demux == NULL)) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * For dvr sources,
+ * verify that this source is connected to the respective demux
+ */
+ dmx_index = mpq_demux - mpq_dmx_info.devices;
+
+ if (*src >= DMX_SOURCE_DVR0) {
+ dvr_index = *src - DMX_SOURCE_DVR0;
+
+ if (dvr_index != dmx_index) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: can't connect demux%d to dvr%d\n",
+ __func__,
+ dmx_index,
+ dvr_index);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * For front-end sources,
+ * verify that this source is not already set to different demux
+ */
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ if ((&mpq_dmx_info.devices[i] != mpq_demux) &&
+ (mpq_dmx_info.devices[i].source == *src)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: demux%d source can't be set,\n"
+ "demux%d occupies this source already\n",
+ __func__,
+ dmx_index,
+ i);
+ return -EBUSY;
+ }
+ }
+
+ mpq_demux->source = *src;
+ return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_set_source);
+
+
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
+{
+ int ret;
+ void *packet_buffer;
+ void *payload_buffer;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_demux *mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+ struct mpq_streambuffer *stream_buffer;
+ int actual_buffer_size;
+
+ /* Allocate memory for private feed data */
+ feed_data = vmalloc(sizeof(struct mpq_video_feed_info));
+
+ if (feed_data == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to private video feed data\n",
+ __func__);
+
+ ret = -ENOMEM;
+ goto init_failed;
+ }
+
+ /* Allocate packet buffer holding the meta-data */
+ packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
+
+ if (packet_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate packets buffer\n",
+ __func__);
+
+ ret = -ENOMEM;
+ goto init_failed_free_priv_data;
+ }
+
+ /*
+ * Allocate payload buffer through ION.
+ * TODO: for scrambling support, need to check if the
+ * stream is scrambled and allocate the buffer with secure
+ * flag set.
+ */
+
+ if (mpq_dmx_info.decoder_data_wrap)
+ actual_buffer_size =
+ feed->buffer_size;
+ else
+ actual_buffer_size =
+ feed->buffer_size + VIDEO_WRAP_AROUND_THRESHOLD;
+
+ actual_buffer_size += (SZ_4K - 1);
+ actual_buffer_size &= ~(SZ_4K - 1);
+
+ feed_data->payload_buff_handle =
+ ion_alloc(mpq_demux->ion_client,
+ actual_buffer_size,
+ SZ_4K,
+ ION_HEAP(ION_CP_MM_HEAP_ID));
+
+ if (IS_ERR_OR_NULL(feed_data->payload_buff_handle)) {
+ ret = PTR_ERR(feed_data->payload_buff_handle);
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate payload buffer %d\n",
+ __func__,
+ ret);
+
+ goto init_failed_free_packet_buffer;
+ }
+
+ payload_buffer =
+ ion_map_kernel(mpq_demux->ion_client,
+ feed_data->payload_buff_handle,
+ 0);
+
+ if (IS_ERR_OR_NULL(payload_buffer)) {
+ ret = PTR_ERR(payload_buffer);
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map payload buffer %d\n",
+ __func__,
+ ret);
+
+ goto init_failed_free_payload_buffer;
+ }
+
+ /* Register the new stream-buffer interface to MPQ adapter */
+ switch (feed->pes_type) {
+ case DMX_TS_PES_VIDEO0:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO0_STREAM_IF;
+ break;
+
+ case DMX_TS_PES_VIDEO1:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO1_STREAM_IF;
+ break;
+
+ case DMX_TS_PES_VIDEO2:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO2_STREAM_IF;
+ break;
+
+ case DMX_TS_PES_VIDEO3:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO3_STREAM_IF;
+ break;
+
+ default:
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid pes type %d\n",
+ __func__,
+ feed->pes_type);
+ ret = -EINVAL;
+ goto init_failed_unmap_payload_buffer;
+ }
+
+ /* 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: Video interface %d already occupied!\n",
+ __func__,
+ feed_data->stream_interface);
+ ret = -EBUSY;
+ goto init_failed_unmap_payload_buffer;
+ }
+
+ feed_data->video_buffer =
+ &mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
+
+ mpq_streambuffer_init(
+ feed_data->video_buffer,
+ payload_buffer,
+ actual_buffer_size,
+ packet_buffer,
+ VIDEO_META_DATA_BUFFER_SIZE);
+
+ ret =
+ mpq_adapter_register_stream_if(
+ feed_data->stream_interface,
+ feed_data->video_buffer);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_adapter_register_stream_if failed, "
+ "err = %d\n",
+ __func__,
+ ret);
+ goto init_failed_unmap_payload_buffer;
+ }
+
+ feed->buffer_size = actual_buffer_size;
+ feed_data->pes_payload_address =
+ (u32)feed_data->video_buffer->raw_data.data;
+
+ feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+ feed_data->pes_header_offset = 0;
+ feed->pusi_seen = 0;
+ feed->peslen = 0;
+ feed_data->fullness_wait_cancel = 0;
+
+ spin_lock(&mpq_demux->feed_lock);
+ feed->priv = (void *)feed_data;
+ spin_unlock(&mpq_demux->feed_lock);
+
+ return 0;
+
+init_failed_unmap_payload_buffer:
+ ion_unmap_kernel(mpq_demux->ion_client,
+ feed_data->payload_buff_handle);
+init_failed_free_payload_buffer:
+ ion_free(mpq_demux->ion_client,
+ feed_data->payload_buff_handle);
+init_failed_free_packet_buffer:
+ vfree(packet_buffer);
+init_failed_free_priv_data:
+ vfree(feed_data);
+ feed->priv = NULL;
+init_failed:
+
+ return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_init_video_feed);
+
+
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
+{
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_demux *mpq_demux;
+
+ if (feed->priv == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed, feed->priv is NULL\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ feed_data =
+ (struct mpq_video_feed_info *)feed->priv;
+
+ spin_lock(&mpq_demux->feed_lock);
+ feed->priv = NULL;
+ spin_unlock(&mpq_demux->feed_lock);
+
+ wake_up_all(&feed_data->video_buffer->raw_data.queue);
+
+ mpq_adapter_unregister_stream_if(
+ feed_data->stream_interface);
+
+ vfree(feed_data->video_buffer->packet_data.data);
+
+ ion_unmap_kernel(mpq_demux->ion_client,
+ feed_data->payload_buff_handle);
+
+ ion_free(mpq_demux->ion_client,
+ feed_data->payload_buff_handle);
+
+ vfree(feed_data);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_terminate_video_feed);
+
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
+{
+ struct mpq_demux *mpq_demux;
+
+ mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ struct mpq_video_feed_info *feed_data;
+
+ spin_lock(&mpq_demux->feed_lock);
+
+ if (feed->priv == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed, feed->priv is NULL\n",
+ __func__);
+ spin_unlock(&mpq_demux->feed_lock);
+ return -EINVAL;
+ }
+
+ feed_data =
+ (struct mpq_video_feed_info *)feed->priv;
+
+ feed_data->fullness_wait_cancel = 0;
+
+ spin_unlock(&mpq_demux->feed_lock);
+
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_DBG_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_init);
+
+int mpq_dmx_decoder_fullness_wait(
+ struct dvb_demux_feed *feed,
+ size_t required_space)
+{
+ struct mpq_demux *mpq_demux;
+
+ mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ int ret;
+ int gap;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_ringbuffer *video_buff;
+
+ spin_lock(&mpq_demux->feed_lock);
+
+ if (feed->priv == NULL) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return -EINVAL;
+ }
+
+ feed_data =
+ (struct mpq_video_feed_info *)feed->priv;
+
+ video_buff =
+ &feed_data->video_buffer->raw_data;
+
+ /*
+ * If we are now starting new PES and the
+ * PES payload may wrap-around, extra padding
+ * needs to be pushed into the buffer.
+ */
+ gap = video_buff->size - video_buff->pwrite;
+ if ((!mpq_dmx_info.decoder_data_wrap) &&
+ (gap < VIDEO_WRAP_AROUND_THRESHOLD))
+ required_space += gap;
+
+ ret = 0;
+ if ((feed_data != NULL) &&
+ (!feed_data->fullness_wait_cancel) &&
+ (dvb_ringbuffer_free(video_buff) < required_space)) {
+ DEFINE_WAIT(__wait);
+ for (;;) {
+ prepare_to_wait(
+ &video_buff->queue,
+ &__wait,
+ TASK_INTERRUPTIBLE);
+
+ if ((feed->priv == NULL) ||
+ (feed_data->fullness_wait_cancel) ||
+ (dvb_ringbuffer_free(video_buff) >=
+ required_space))
+ break;
+
+ if (!signal_pending(current)) {
+ spin_unlock(&mpq_demux->feed_lock);
+ schedule();
+ spin_lock(&mpq_demux->feed_lock);
+ continue;
+ }
+ ret = -ERESTARTSYS;
+ break;
+ }
+ finish_wait(&video_buff->queue, &__wait);
+ }
+
+ if (ret < 0) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return ret;
+ }
+
+ if ((feed->priv == NULL) ||
+ (feed_data->fullness_wait_cancel)) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return -EINVAL;
+ }
+
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_DBG_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
+
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
+{
+ struct mpq_demux *mpq_demux;
+
+ mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_ringbuffer *video_buff;
+
+ spin_lock(&mpq_demux->feed_lock);
+
+ if (feed->priv == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed, feed->priv is NULL\n",
+ __func__);
+ spin_unlock(&mpq_demux->feed_lock);
+ return -EINVAL;
+ }
+
+ feed_data =
+ (struct mpq_video_feed_info *)feed->priv;
+
+ video_buff =
+ &feed_data->video_buffer->raw_data;
+
+ feed_data->fullness_wait_cancel = 1;
+ spin_unlock(&mpq_demux->feed_lock);
+
+ wake_up_all(&video_buff->queue);
+
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_abort);
+
+int mpq_dmx_process_video_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ int bytes_avail;
+ int left_size;
+ int copy_len;
+ u32 ts_payload_offset;
+ struct mpq_video_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;
+
+ mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ spin_lock(&mpq_demux->feed_lock);
+
+ feed_data =
+ (struct mpq_video_feed_info *)feed->priv;
+
+ if (unlikely(feed_data == NULL)) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ ts_header = (const struct ts_packet_header *)buf;
+
+ stream_buffer =
+ feed_data->video_buffer;
+
+ pes_header =
+ &feed_data->pes_header;
+
+/* printk("TS packet: %X %X %X %X %X%X %X %X %X\n",
+ ts_header->sync_byte,
+ ts_header->transport_error_indicator,
+ ts_header->payload_unit_start_indicator,
+ ts_header->transport_priority,
+ ts_header->pid_msb,
+ ts_header->pid_lsb,
+ ts_header->transport_scrambling_control,
+ ts_header->adaptation_field_control,
+ ts_header->continuity_counter);*/
+
+ /* 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(&mpq_demux->feed_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_video_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 (0 == feed_data->pes_header_left_bytes) {
+ packet.raw_data_addr =
+ feed_data->pes_payload_address;
+
+ packet.raw_data_len = feed->peslen;
+
+ if ((!mpq_dmx_info.decoder_data_wrap) &&
+ ((feed_data->pes_payload_address +
+ feed->peslen) >
+ ((u32)stream_buffer->raw_data.data +
+ stream_buffer->raw_data.size)))
+ MPQ_DVB_ERR_PRINT(
+ "%s: "
+ "Video data has wrapped-around!\n",
+ __func__);
+
+ packet.user_data_len =
+ sizeof(struct
+ mpq_adapter_video_meta_data);
+
+ if ((pes_header->pts_dts_flag == 2) ||
+ (pes_header->pts_dts_flag == 3))
+ meta_data.pts_exist = 1;
+ else
+ meta_data.pts_exist = 0;
+
+ meta_data.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;
+
+ if (pes_header->pts_dts_flag == 3)
+ meta_data.dts_exist = 1;
+ else
+ meta_data.dts_exist = 0;
+
+ meta_data.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;
+
+ meta_data.is_padding = 0;
+
+ if (mpq_streambuffer_pkt_write(
+ stream_buffer,
+ &packet,
+ (u8 *)&meta_data) < 0)
+ MPQ_DVB_ERR_PRINT(
+ "%s: "
+ "Couldn't write packet. "
+ "Should never happen\n",
+ __func__);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: received PUSI"
+ "while handling PES header"
+ "of previous PES\n",
+ __func__);
+ }
+
+ /* Reset PES info */
+ feed_data->pes_payload_address =
+ (u32)stream_buffer->raw_data.data +
+ stream_buffer->raw_data.pwrite;
+
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes =
+ PES_MANDATORY_FIELDS_LEN;
+ } else {
+ feed->pusi_seen = 1;
+ }
+ }
+
+ /*
+ * Parse PES data only if PUSI was encountered,
+ * otherwise the data is dropped
+ */
+ if (!feed->pusi_seen) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0; /* drop and wait for next packets */
+ }
+
+ ts_payload_offset = sizeof(struct ts_packet_header);
+
+ /* Skip adaptation field if exists */
+ if (ts_header->adaptation_field_control == 3)
+ ts_payload_offset += buf[ts_payload_offset] + 1;
+
+ bytes_avail = 188 - ts_payload_offset;
+
+ /* Got the mandatory fields of the video PES header? */
+ 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 *)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) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ /* 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_video_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__);
+
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ feed_data->pes_header_left_bytes =
+ pes_header->pes_header_data_length;
+ }
+
+ /* Remainning header bytes that need to be processed? */
+ if (feed_data->pes_header_left_bytes) {
+ /* Did we capture the PTS value (if exist)? */
+ 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 *)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) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ /* 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 *)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) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ /* 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;
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ /* 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;
+ }
+
+ /*
+ * If we reached here,
+ * then we are now at the PES payload data
+ */
+ if (bytes_avail == 0) {
+ spin_unlock(&mpq_demux->feed_lock);
+ return 0;
+ }
+
+ if (feed->peslen == 0) { /* starting new PES */
+ /* gap till end of the buffer */
+ int gap =
+ stream_buffer->raw_data.size -
+ stream_buffer->raw_data.pwrite;
+
+ if ((!mpq_dmx_info.decoder_data_wrap) &&
+ (gap < VIDEO_WRAP_AROUND_THRESHOLD)) {
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_adapter_video_meta_data meta_data;
+
+ /*
+ * Do not start writting new PES from
+ * this location to prevent possible
+ * wrap-around of the payload, fill padding instead.
+ */
+
+ /* push a packet with padding indication */
+ meta_data.is_padding = 1;
+
+ packet.raw_data_len = gap;
+ packet.user_data_len =
+ sizeof(struct mpq_adapter_video_meta_data);
+ packet.raw_data_addr =
+ feed_data->pes_payload_address;
+
+ if (mpq_streambuffer_data_write_deposit(
+ stream_buffer,
+ gap) < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_data_write_deposit "
+ "failed!\n",
+ __func__);
+ } else if (mpq_streambuffer_pkt_write(
+ stream_buffer,
+ &packet,
+ (u8 *)&meta_data) < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: "
+ "Couldn't write packet. "
+ "Should never happen\n",
+ __func__);
+ } else {
+ feed_data->pes_payload_address =
+ (u32)stream_buffer->raw_data.data +
+ stream_buffer->raw_data.pwrite;
+ }
+ }
+ }
+
+ if (mpq_streambuffer_data_write(
+ stream_buffer,
+ buf+ts_payload_offset,
+ bytes_avail) < 0)
+ mpq_demux->decoder_tsp_drop_count++;
+ else
+ feed->peslen += bytes_avail;
+
+ spin_unlock(&mpq_demux->feed_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_process_video_packet);
+
+
+int mpq_dmx_process_pcr_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ int i;
+ u64 pcr;
+ u64 stc;
+ u8 output[PCR_STC_LEN];
+ struct mpq_demux *mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+ const struct ts_packet_header *ts_header;
+ const struct ts_adaptation_field *adaptation_field;
+
+ /*
+ * When we play from front-end, we configure HW
+ * to output the extra timestamp, if we are playing
+ * from DVR, make sure the format is 192 packet.
+ */
+ if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
+ (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid packet format %d for PCR extraction\n",
+ __func__,
+ mpq_demux->demux.tsp_format);
+
+ return -EINVAL;
+ }
+
+ ts_header = (const struct ts_packet_header *)buf;
+
+ /* Make sure this TS packet has a adaptation field */
+ if ((ts_header->sync_byte != 0x47) ||
+ (ts_header->adaptation_field_control == 0) ||
+ (ts_header->adaptation_field_control == 1)) {
+ return 0;
+ }
+
+ adaptation_field = (const struct ts_adaptation_field *)
+ (buf + sizeof(struct ts_packet_header));
+
+ if ((!adaptation_field->adaptation_field_length) ||
+ (!adaptation_field->PCR_flag))
+ return 0; /* 0 adaptation field or no PCR */
+
+ pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
+ pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
+ pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
+ pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
+ pcr += adaptation_field->program_clock_reference_base_5;
+ pcr *= 300;
+ pcr +=
+ (((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
+ adaptation_field->program_clock_reference_ext_2;
+
+ stc = buf[189] << 16;
+ stc += buf[190] << 8;
+ stc += buf[191];
+ stc *= 256; /* convert from 105.47 KHZ to 27MHz */
+
+ output[0] = adaptation_field->discontinuity_indicator;
+
+ for (i = 1; i <= 8; i++)
+ output[i] = (stc >> ((8-i) << 3)) & 0xFF;
+
+ for (i = 9; i <= 16; i++)
+ output[i] = (pcr >> ((16-i) << 3)) & 0xFF;
+
+ feed->cb.ts(output, PCR_STC_LEN,
+ NULL, 0,
+ &feed->feed.ts, DMX_OK);
+ return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
new file mode 100644
index 0000000..d90bd89
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -0,0 +1,494 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DMX_PLUGIN_COMMON_H
+#define _MPQ_DMX_PLUGIN_COMMON_H
+
+#include <linux/ion.h>
+
+#include "dvbdev.h"
+#include "dmxdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "mpq_adapter.h"
+
+
+/**
+ * Total number of filters per demux,
+ * including section and PES feeds
+ */
+#define MPQ_DMX_MAX_NUM_OF_FILTERS 64
+
+/**
+ * TSIF alias name length
+ */
+#define TSIF_NAME_LENGTH 10
+
+/**
+ * struct mpq_demux - mpq demux information
+ * @demux: The dvb_demux instance used by mpq_demux
+ * @dmxdev: The dmxdev instance used by mpq_demux
+ * @fe_memory: Handle of front-end memory source to mpq_demux
+ * @source: The current source connected to the demux
+ * @is_initialized: Indicates whether this demux device was
+ * initialized or not.
+ * @ion_client: ION demux client used to allocate memory from ION.
+ * @feed_lock: Lock used to protect against private feed data
+ * @hw_notification_rate: Notification rate in msec, exposed in debugfs.
+ * @hw_notification_count: Notification count, exposed in debugfs.
+ * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @decoder_tsp_drop_count: Counter of number of dropped TS packets
+ * due to decoder buffer fullness, exposed in debugfs.
+ * @last_notification_time: Time of last HW notification.
+ */
+struct mpq_demux {
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_memory;
+ dmx_source_t source;
+ int is_initialized;
+ struct ion_client *ion_client;
+ spinlock_t feed_lock;
+
+ /* debug-fs */
+ u32 hw_notification_rate;
+ u32 hw_notification_count;
+ u32 hw_notification_size;
+ u32 decoder_tsp_drop_count;
+ struct timespec last_notification_time;
+};
+
+/**
+ * mpq_dmx_init - initialization and registration function of
+ * single MPQ demux device
+ *
+ * @adapter: The adapter to register mpq_demux to
+ * @mpq_demux: The mpq demux to initialize
+ *
+ * Every HW pluging need to provide implementation of such
+ * function that will be called for each demux device on the
+ * module initialization. The function mpq_demux_plugin_init
+ * should be called during the HW plugin module initialization.
+ */
+typedef int (*mpq_dmx_init)(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *demux);
+
+/**
+ * struct ts_packet_header - Transport packet header
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned sync_byte:8;
+ unsigned transport_error_indicator:1;
+ unsigned payload_unit_start_indicator:1;
+ unsigned transport_priority:1;
+ unsigned pid_msb:5;
+ unsigned pid_lsb:8;
+ unsigned transport_scrambling_control:2;
+ unsigned adaptation_field_control:2;
+ unsigned continuity_counter:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned sync_byte:8;
+ unsigned pid_msb:5;
+ unsigned transport_priority:1;
+ unsigned payload_unit_start_indicator:1;
+ unsigned transport_error_indicator:1;
+ unsigned pid_lsb:8;
+ unsigned continuity_counter:4;
+ unsigned adaptation_field_control:2;
+ unsigned transport_scrambling_control:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/**
+ * struct ts_adaptation_field - Adaptation field prefix
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_adaptation_field {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned adaptation_field_length:8;
+ unsigned discontinuity_indicator:1;
+ unsigned random_access_indicator:1;
+ unsigned elementary_stream_priority_indicator:1;
+ unsigned PCR_flag:1;
+ unsigned OPCR_flag:1;
+ unsigned splicing_point_flag:1;
+ unsigned transport_private_data_flag:1;
+ unsigned adaptation_field_extension_flag:1;
+ unsigned program_clock_reference_base_1:8;
+ unsigned program_clock_reference_base_2:8;
+ unsigned program_clock_reference_base_3:8;
+ unsigned program_clock_reference_base_4:8;
+ unsigned program_clock_reference_base_5:1;
+ unsigned reserved:6;
+ unsigned program_clock_reference_ext_1:1;
+ unsigned program_clock_reference_ext_2:8;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned adaptation_field_length:8;
+ unsigned adaptation_field_extension_flag:1;
+ unsigned transport_private_data_flag:1;
+ unsigned splicing_point_flag:1;
+ unsigned OPCR_flag:1;
+ unsigned PCR_flag:1;
+ unsigned elementary_stream_priority_indicator:1;
+ unsigned random_access_indicator:1;
+ unsigned discontinuity_indicator:1;
+ unsigned program_clock_reference_base_1:8;
+ unsigned program_clock_reference_base_2:8;
+ unsigned program_clock_reference_base_3:8;
+ unsigned program_clock_reference_base_4:8;
+ unsigned program_clock_reference_ext_1:1;
+ unsigned reserved:6;
+ unsigned program_clock_reference_base_5:1;
+ unsigned program_clock_reference_ext_2:8;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+
+/*
+ * PES packet header containing dts and/or pts values
+ * as defined in MPEG2 transport stream standard.
+ */
+struct pes_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned packet_start_code_prefix_1:8;
+ unsigned packet_start_code_prefix_2:8;
+ unsigned packet_start_code_prefix_3:8;
+ unsigned stream_id:8;
+ unsigned pes_packet_length_msb:8;
+ unsigned pes_packet_length_lsb:8;
+ unsigned reserved_bits0:2;
+ unsigned pes_scrambling_control:2;
+ unsigned pes_priority:1;
+ unsigned data_alignment_indicator:1;
+ unsigned copyright:1;
+ unsigned original_or_copy:1;
+ unsigned pts_dts_flag:2;
+ unsigned escr_flag:1;
+ unsigned es_rate_flag:1;
+ unsigned dsm_trick_mode_flag:1;
+ unsigned additional_copy_info_flag:1;
+ unsigned pes_crc_flag:1;
+ unsigned pes_extension_flag:1;
+ unsigned pes_header_data_length:8;
+ unsigned reserved_bits1:4;
+ unsigned pts_1:3;
+ unsigned marker_bit0:1;
+ unsigned pts_2:8;
+ unsigned pts_3:7;
+ unsigned marker_bit1:1;
+ unsigned pts_4:8;
+ unsigned pts_5:7;
+ unsigned marker_bit2:1;
+ unsigned reserved_bits2:4;
+ unsigned dts_1:3;
+ unsigned marker_bit3:1;
+ unsigned dts_2:8;
+ unsigned dts_3:7;
+ unsigned marker_bit4:1;
+ unsigned dts_4:8;
+ unsigned dts_5:7;
+ unsigned marker_bit5:1;
+ unsigned reserved_bits3:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned packet_start_code_prefix_1:8;
+ unsigned packet_start_code_prefix_2:8;
+ unsigned packet_start_code_prefix_3:8;
+ unsigned stream_id:8;
+ unsigned pes_packet_length_lsb:8;
+ unsigned pes_packet_length_msb:8;
+ unsigned original_or_copy:1;
+ unsigned copyright:1;
+ unsigned data_alignment_indicator:1;
+ unsigned pes_priority:1;
+ unsigned pes_scrambling_control:2;
+ unsigned reserved_bits0:2;
+ unsigned pes_extension_flag:1;
+ unsigned pes_crc_flag:1;
+ unsigned additional_copy_info_flag:1;
+ unsigned dsm_trick_mode_flag:1;
+ unsigned es_rate_flag:1;
+ unsigned escr_flag:1;
+ unsigned pts_dts_flag:2;
+ unsigned pes_header_data_length:8;
+ unsigned marker_bit0:1;
+ unsigned pts_1:3;
+ unsigned reserved_bits1:4;
+ unsigned pts_2:8;
+ unsigned marker_bit1:1;
+ unsigned pts_3:7;
+ unsigned pts_4:8;
+ unsigned marker_bit2:1;
+ unsigned pts_5:7;
+ unsigned marker_bit3:1;
+ unsigned dts_1:3;
+ unsigned reserved_bits2:4;
+ unsigned dts_2:8;
+ unsigned marker_bit4:1;
+ unsigned dts_3:7;
+ unsigned dts_4:8;
+ unsigned marker_bit5:1;
+ unsigned dts_5:7;
+ unsigned reserved_bits3:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/*
+ * mpq_video_feed_info - private data used for video feed.
+ *
+ * @plugin_data: Underlying plugin's own private data.
+ * @video_buffer: Holds the streamer buffer shared with
+ * the decoder for feeds having the data going to the decoder.
+ * @pes_header: Used for feeds that output data to decoder,
+ * holds PES header of current processed PES.
+ * @pes_header_left_bytes: Used for feeds that output data to decoder,
+ * holds remainning PES header bytes of current processed PES.
+ * @pes_header_offset: Holds the offset within the current processed
+ * pes header.
+ * @fullness_wait_cancel: Flag used to signal to abort waiting for
+ * decoder's fullness.
+ * @pes_payload_address: Used for feeds that output data to decoder,
+ * holds current PES payload start address.
+ * @payload_buff_handle: ION handle for the allocated payload buffer
+ * @stream_interface: The ID of the video stream interface registered
+ * with this stream buffer.
+ */
+struct mpq_video_feed_info {
+ void *plugin_data;
+ struct mpq_streambuffer *video_buffer;
+ struct pes_packet_header pes_header;
+ u32 pes_header_left_bytes;
+ u32 pes_header_offset;
+ u32 pes_payload_address;
+ int fullness_wait_cancel;
+ struct ion_handle *payload_buff_handle;
+ enum mpq_adapter_stream_if stream_interface;
+};
+
+/**
+ * mpq_demux_plugin_init - Initialize demux devices and register
+ * them to the dvb adapter.
+ *
+ * @dmx_init_func: Pointer to the function to be used
+ * to initialize demux of the udnerlying HW plugin.
+ *
+ * Return error code
+ *
+ * Should be called at the HW plugin module initialization.
+ */
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func);
+
+/**
+ * mpq_demux_plugin_exit - terminate demux devices.
+ *
+ * Should be called at the HW plugin module termination.
+ */
+void mpq_dmx_plugin_exit(void);
+
+/**
+ * mpq_dmx_set_source - implmenetation of set_source routine.
+ *
+ * @demux: The demux device to set its source.
+ * @src: The source to be set.
+ *
+ * Return error code
+ *
+ * Can be used by the underlying plugins to implement kernel
+ * demux API set_source routine.
+ */
+int mpq_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src);
+
+/**
+ * mpq_dmx_init_video_feed - Initializes video feed
+ * used to pass data to decoder directly.
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return error code.
+ *
+ * If the underlying plugin wishes to perform SW PES assmebly
+ * for the video data and stream it to the decoder, it should
+ * call this function when video feed is initialized before
+ * using mpq_dmx_process_video_packet.
+ *
+ * The function allocates mpq_video_feed_info and saves in
+ * feed->priv.
+ */
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_terminate_video_feed - Free private data of
+ * video feed allocated in mpq_dmx_init_video_feed
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return error code.
+ */
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_init - Initialize waiting
+ * mechanism on decoder's buffer fullness.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_init(
+ struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_wait - Checks whether decoder buffer
+ * have free space as required, if not, wait for it.
+ *
+ * @feed: The decoder's feed
+ * @required_space: the required free space to wait for
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_wait(
+ struct dvb_demux_feed *feed,
+ size_t required_space);
+
+/**
+ * mpq_dmx_decoder_fullness_abort - Aborts waiting
+ * on decoder's buffer fullness if any waiting is done
+ * now. After calling this, to wait again the user must
+ * call mpq_dmx_decoder_fullness_init.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_abort(
+ struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_process_video_packet - Assemble PES data and output it
+ * to the stream-buffer connected to the decoder.
+ *
+ * @feed: The feed used for the video TS packets
+ * @buf: The buffer holding video TS packet.
+ *
+ * Return error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID.
+ * If the output buffer is full while assembly, the function drops
+ * the packet and does not write them to the output buffer.
+ * Scrambled packets are bypassed.
+ */
+int mpq_dmx_process_video_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf);
+
+/**
+ * mpq_dmx_process_pcr_packet - Extract PCR/STC pairs from
+ * a 192 bytes packet.
+ *
+ * @feed: The feed used for the PCR TS packets
+ * @buf: The buffer holding pcr/stc packet.
+ *
+ * Return error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID, and that it has 4 bytes
+ * suffix as extra timestamp in the following format:
+ *
+ * Byte3: TSIF flags
+ * Byte0-2: TTS, 0..2^24-1 at 105.47 Khz (27*10^6/256).
+ *
+ * The function callbacks dmxdev after extraction of the pcr/stc
+ * pair.
+ */
+int mpq_dmx_process_pcr_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf);
+
+/**
+ * mpq_dmx_is_video_feed - Returns whether the PES feed
+ * is video one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is video feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_video_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_TS_PES_VIDEO0) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO1) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO2) ||
+ (feed->pes_type == DMX_TS_PES_VIDEO3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mpq_dmx_is_pcr_feed - Returns whether the PES feed
+ * is PCR one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is PCR feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_pcr_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_TS_PES_PCR0) ||
+ (feed->pes_type == DMX_TS_PES_PCR1) ||
+ (feed->pes_type == DMX_TS_PES_PCR2) ||
+ (feed->pes_type == DMX_TS_PES_PCR3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mpq_dmx_init_hw_statistics -
+ * Extend dvb-demux debugfs with HW statistics.
+ *
+ * @mpq_demux: The mpq_demux device to initialize.
+ */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux);
+
+
+/**
+ * mpq_dmx_update_hw_statistics -
+ * Update dvb-demux debugfs with HW notification statistics.
+ *
+ * @mpq_demux: The mpq_demux device to update.
+ */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
+
+#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
new file mode 100644
index 0000000..5894a65
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -0,0 +1,747 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/tsif_api.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+/* TSIF HW configuration: */
+#define TSIF_COUNT 2
+/* When TSIF driver notifies demux that new packets are received */
+#define DMX_TSIF_PACKETS_IN_CHUNK_DEF 16
+#define DMX_TSIF_CHUNKS_IN_BUF 8
+#define DMX_TSIF_TIME_LIMIT 10000
+/* TSIF_DRIVER_MODE: 3 means manual control from debugfs. use 1 normally. */
+#define DMX_TSIF_DRIVER_MODE_DEF 1
+
+
+/* module parameters for load time configuration: */
+static int threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+static int mode = DMX_TSIF_DRIVER_MODE_DEF;
+module_param(threshold, int, S_IRUGO);
+module_param(mode, int, S_IRUGO);
+
+/*
+ * Work scheduled each time TSIF notifies dmx
+ * of new TS packet
+ */
+struct tsif_work {
+ struct work_struct work;
+ int tsif_id;
+};
+
+
+/*
+ * TSIF driver information
+ */
+struct tsif_driver_info {
+ /* handler to TSIF driver */
+ void *tsif_handler;
+ /* TSIF driver data buffer pointer */
+ void *data_buffer;
+ /* TSIF driver data buffer size, in packets */
+ int buffer_size;
+ /* TSIF driver read pointer */
+ int ri;
+ /* TSIF driver write pointer */
+ int wi;
+ /* TSIF driver state */
+ enum tsif_state state;
+};
+
+
+/*
+ * The following structure hold singelton information
+ * required for dmx implementation on top of TSIF.
+ */
+static struct
+{
+ /* Information for each TSIF input processing */
+ struct {
+ /* work used to submit to workqueue for processing */
+ struct tsif_work work;
+
+ /* workqueue that processes TS packets from specific TSIF */
+ struct workqueue_struct *workqueue;
+
+ /* TSIF alias */
+ char name[TSIF_NAME_LENGTH];
+
+ /* TSIF driver information */
+ struct tsif_driver_info tsif_driver;
+
+ /* TSIF reference count (counts start/stop operations */
+ int ref_count;
+
+ /* Pointer to the demux connected to this TSIF */
+ struct mpq_demux *mpq_demux;
+
+ /* mutex protecting the data-structure */
+ struct mutex mutex;
+ } tsif[TSIF_COUNT];
+} mpq_dmx_tsif_info;
+
+
+/**
+ * Worker function that processes the TS packets notified by the TSIF driver.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tsif_work(struct work_struct *worker)
+{
+ struct tsif_work *tsif_work =
+ container_of(worker, struct tsif_work, work);
+ struct mpq_demux *mpq_demux;
+ struct tsif_driver_info *tsif_driver;
+ size_t packets = 0;
+ int tsif = tsif_work->tsif_id;
+
+ mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+ tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+ MPQ_DVB_DBG_PRINT(
+ "%s executed, tsif = %d\n",
+ __func__,
+ tsif);
+
+ if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+ return;
+
+ /* Check if driver handler is still valid */
+ if (tsif_driver->tsif_handler == NULL) {
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+ MPQ_DVB_ERR_PRINT("%s: tsif_driver->tsif_handler is NULL!\n",
+ __func__);
+ return;
+ }
+
+ tsif_get_state(tsif_driver->tsif_handler, &(tsif_driver->ri),
+ &(tsif_driver->wi), &(tsif_driver->state));
+
+ if ((tsif_driver->wi == tsif_driver->ri) ||
+ (tsif_driver->state == tsif_state_stopped) ||
+ (tsif_driver->state == tsif_state_error)) {
+
+ mpq_demux->hw_notification_size = 0;
+
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid TSIF state (%d), wi = (%d), ri = (%d)\n",
+ __func__,
+ tsif_driver->state, tsif_driver->wi, tsif_driver->ri);
+ return;
+ }
+
+ if (tsif_driver->wi > tsif_driver->ri) {
+ packets = (tsif_driver->wi - tsif_driver->ri);
+ mpq_demux->hw_notification_size = packets;
+
+ dvb_dmx_swfilter_format(
+ &mpq_demux->demux,
+ (tsif_driver->data_buffer +
+ (tsif_driver->ri * TSIF_PKT_SIZE)),
+ (packets * TSIF_PKT_SIZE),
+ DMX_TSP_FORMAT_192_TAIL);
+
+ tsif_driver->ri =
+ (tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+ tsif_reclaim_packets(tsif_driver->tsif_handler,
+ tsif_driver->ri);
+ } else {
+ /*
+ * wi < ri, means wraparound on cyclic buffer.
+ * Handle in two stages.
+ */
+ packets = (tsif_driver->buffer_size - tsif_driver->ri);
+ mpq_demux->hw_notification_size = packets;
+
+ dvb_dmx_swfilter_format(
+ &mpq_demux->demux,
+ (tsif_driver->data_buffer +
+ (tsif_driver->ri * TSIF_PKT_SIZE)),
+ (packets * TSIF_PKT_SIZE),
+ DMX_TSP_FORMAT_192_TAIL);
+
+ /* tsif_driver->ri should be 0 after this */
+ tsif_driver->ri =
+ (tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+ packets = tsif_driver->wi;
+ if (packets > 0) {
+ mpq_demux->hw_notification_size += packets;
+
+ dvb_dmx_swfilter_format(
+ &mpq_demux->demux,
+ (tsif_driver->data_buffer +
+ (tsif_driver->ri * TSIF_PKT_SIZE)),
+ (packets * TSIF_PKT_SIZE),
+ DMX_TSP_FORMAT_192_TAIL);
+
+ tsif_driver->ri =
+ (tsif_driver->ri + packets) %
+ tsif_driver->buffer_size;
+ }
+
+ tsif_reclaim_packets(tsif_driver->tsif_handler,
+ tsif_driver->ri);
+ }
+
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+}
+
+
+/**
+ * Callback function from TSIF driver when new data is ready.
+ *
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tsif_callback(void *user)
+{
+ int tsif = (int)user;
+ struct work_struct *work;
+ struct mpq_demux *mpq_demux;
+
+ MPQ_DVB_DBG_PRINT("%s executed, tsif = %d\n", __func__, tsif);
+
+ /* Save statistics on TSIF notifications */
+ mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+ mpq_dmx_update_hw_statistics(mpq_demux);
+
+ work = &mpq_dmx_tsif_info.tsif[tsif].work.work;
+
+ /* Scheudle a new work to demux workqueue */
+ if (!work_pending(work))
+ queue_work(mpq_dmx_tsif_info.tsif[tsif].workqueue, work);
+}
+
+
+/**
+ * Attach to TSIF driver and start TSIF operation.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return error code.
+ */
+static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux)
+{
+ int ret = 0;
+ int tsif;
+ struct tsif_driver_info *tsif_driver;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+ tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+ /* Attach to TSIF driver */
+
+ tsif_driver->tsif_handler =
+ tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
+ if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
+ tsif_driver->tsif_handler = NULL;
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+ MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n",
+ __func__, tsif);
+ return -ENODEV;
+ }
+
+ /* Set TSIF driver mode */
+ ret = tsif_set_mode(tsif_driver->tsif_handler,
+ mode);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
+ __func__, mode);
+ }
+
+ /* Set TSIF buffer configuration */
+ ret = tsif_set_buf_config(tsif_driver->tsif_handler,
+ threshold,
+ DMX_TSIF_CHUNKS_IN_BUF);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tsif_set_buf_config (%d, %d) failed\n",
+ __func__, threshold,
+ DMX_TSIF_CHUNKS_IN_BUF);
+ MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
+ }
+
+
+ /* Set TSIF driver time limit */
+ /* TODO: needed?? */
+/* ret = tsif_set_time_limit(tsif_driver->tsif_handler,
+ DMX_TSIF_TIME_LIMIT);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tsif_set_time_limit (%d) failed\n",
+ __func__, DMX_TSIF_TIME_LIMIT);
+ }
+*/
+
+ /* Start TSIF driver */
+ ret = tsif_start(tsif_driver->tsif_handler);
+ if (ret < 0) {
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+ MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__);
+ return ret;
+ }
+
+ /*
+ * Get data buffer information from TSIF driver
+ * (must be called after tsif_start)
+ */
+ tsif_get_info(tsif_driver->tsif_handler,
+ &(tsif_driver->data_buffer),
+ &(tsif_driver->buffer_size));
+
+ /* save pointer to the mpq_demux we are working on */
+ mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux;
+ }
+ mpq_dmx_tsif_info.tsif[tsif].ref_count++;
+
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+ return ret;
+}
+
+
+/**
+ * Stop TSIF operation and detach from TSIF driver.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return error code.
+ */
+static int mpq_tsif_dmx_stop(struct mpq_demux *mpq_demux)
+{
+ int tsif;
+ struct tsif_driver_info *tsif_driver;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ mpq_dmx_tsif_info.tsif[tsif].ref_count--;
+
+ if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+ tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+ tsif_stop(tsif_driver->tsif_handler);
+ tsif_detach(tsif_driver->tsif_handler);
+ /*
+ * temporarily release mutex and flush the work queue
+ * before setting tsif_handler to NULL
+ */
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+ flush_workqueue(mpq_dmx_tsif_info.tsif[tsif].workqueue);
+ /* re-acquire mutex */
+ if (mutex_lock_interruptible(
+ &mpq_dmx_tsif_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ tsif_driver->tsif_handler = NULL;
+ tsif_driver->data_buffer = NULL;
+ tsif_driver->buffer_size = 0;
+ mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL;
+ }
+
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+ return 0;
+}
+
+
+/**
+ * Start filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return error code.
+ */
+static int mpq_tsif_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid mpq_demux handle\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* Source from TSIF, need to configure TSIF hardware */
+ ret = mpq_tsif_dmx_start(mpq_demux);
+
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_tsif_dmx_start failed(%d)\n",
+ __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ /* Always feed sections/PES starting from a new one and
+ * do not partial transfer data from older one
+ */
+ feed->pusi_seen = 0;
+
+ /*
+ * For video PES, data is tunneled to the decoder,
+ * initialize tunneling and pes parsing.
+ */
+ if (mpq_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_init_video_feed(feed);
+
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_init_video_feed failed(%d)\n",
+ __func__,
+ ret);
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0)
+ mpq_tsif_dmx_stop(mpq_demux);
+
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * Stop filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return error code.
+ */
+static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid mpq_demux handle\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ /*
+ * For video PES, data is tunneled to the decoder,
+ * terminate tunnel and pes parsing.
+ */
+ if (mpq_dmx_is_video_feed(feed))
+ mpq_dmx_terminate_video_feed(feed);
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* Source from TSIF, need to configure TSIF hardware */
+ ret = mpq_tsif_dmx_stop(mpq_demux);
+ }
+
+ return ret;
+}
+
+
+/**
+ * TSIF demux plugin write-to-decoder function.
+ *
+ * @feed: The feed we are working on.
+ * @buf: The data buffer to process.
+ * @len: The data buffer length.
+ *
+ * Return error code
+ */
+static int mpq_tsif_dmx_write_to_decoder(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ size_t len)
+{
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /*
+ * It is assumed that this function is called once for each
+ * TS packet of the relevant feed.
+ */
+ if (len > TSIF_PKT_SIZE)
+ MPQ_DVB_DBG_PRINT(
+ "%s: warnning - len larger than one packet\n",
+ __func__);
+
+ if (mpq_dmx_is_video_feed(feed))
+ return mpq_dmx_process_video_packet(feed, buf);
+
+ if (mpq_dmx_is_pcr_feed(feed))
+ return mpq_dmx_process_pcr_packet(feed, buf);
+
+ return 0;
+}
+
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return error code
+ */
+static int mpq_tsif_dmx_init(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *mpq_demux)
+{
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* Set the kernel-demux object capabilities */
+ mpq_demux->demux.dmx.capabilities =
+ DMX_TS_FILTERING |
+ DMX_PES_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING |
+ DMX_CRC_CHECKING |
+ DMX_TS_DESCRAMBLING;
+
+ /* Set dvb-demux "virtual" function pointers */
+ mpq_demux->demux.priv = (void *)mpq_demux;
+ mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.start_feed = mpq_tsif_dmx_start_filtering;
+ mpq_demux->demux.stop_feed = mpq_tsif_dmx_stop_filtering;
+ mpq_demux->demux.write_to_decoder = mpq_tsif_dmx_write_to_decoder;
+
+ mpq_demux->demux.decoder_fullness_init =
+ mpq_dmx_decoder_fullness_init;
+
+ mpq_demux->demux.decoder_fullness_wait =
+ mpq_dmx_decoder_fullness_wait;
+
+ mpq_demux->demux.decoder_fullness_abort =
+ mpq_dmx_decoder_fullness_abort;
+
+ /* Initialize dvb_demux object */
+ result = dvb_dmx_init(&mpq_demux->demux);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+ goto init_failed;
+ }
+
+ /* Now initailize the dmx-dev object */
+ mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+ mpq_demux->dmxdev.capabilities =
+ DMXDEV_CAP_DUPLEX |
+ DMXDEV_CAP_PULL_MODE |
+ DMXDEV_CAP_PCR_EXTRACTION;
+
+ mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+ result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+ __func__,
+ result);
+ goto init_failed_dmx_release;
+ }
+
+ /* Extend dvb-demux debugfs with TSIF statistics. */
+ mpq_dmx_init_hw_statistics(mpq_demux);
+
+ return 0;
+
+init_failed_dmx_release:
+ dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+ return result;
+}
+
+
+/**
+ * Module initialization function.
+ *
+ * Return error code
+ */
+static int __init mpq_dmx_tsif_plugin_init(void)
+{
+ int i;
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* check module parameters validity */
+ if (threshold < 1) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid threshold parameter, using %d instead\n",
+ __func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF);
+ threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+ }
+ if ((mode < 1) || (mode > 3)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid mode parameter, using %d instead\n",
+ __func__, DMX_TSIF_DRIVER_MODE_DEF);
+ mode = DMX_TSIF_DRIVER_MODE_DEF;
+ }
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mpq_dmx_tsif_info.tsif[i].work.tsif_id = i;
+
+ INIT_WORK(&mpq_dmx_tsif_info.tsif[i].work.work,
+ mpq_dmx_tsif_work);
+
+ snprintf(mpq_dmx_tsif_info.tsif[i].name,
+ TSIF_NAME_LENGTH,
+ "tsif_%d",
+ i);
+
+ mpq_dmx_tsif_info.tsif[i].workqueue =
+ create_singlethread_workqueue(
+ mpq_dmx_tsif_info.tsif[i].name);
+
+ if (mpq_dmx_tsif_info.tsif[i].workqueue == NULL) {
+ int j;
+
+ for (j = 0; j < i; j++) {
+ destroy_workqueue(
+ mpq_dmx_tsif_info.tsif[j].workqueue);
+ mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex);
+ }
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: create_singlethread_workqueue failed\n",
+ __func__);
+
+ return -ENOMEM;
+ }
+
+ mutex_init(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+ mpq_dmx_tsif_info.tsif[i].tsif_driver.tsif_handler = NULL;
+ mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+ }
+
+ ret = mpq_dmx_plugin_init(mpq_tsif_dmx_init);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+ __func__,
+ ret);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+ mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * Module exit function.
+ */
+static void __exit mpq_dmx_tsif_plugin_exit(void)
+{
+ int i;
+ struct tsif_driver_info *tsif_driver;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mutex_lock(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+ tsif_driver = &(mpq_dmx_tsif_info.tsif[i].tsif_driver);
+ if (mpq_dmx_tsif_info.tsif[i].ref_count > 0) {
+ mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+ if (tsif_driver->tsif_handler)
+ tsif_stop(tsif_driver->tsif_handler);
+ }
+ /* Detach from TSIF driver to avoid further notifications. */
+ if (tsif_driver->tsif_handler)
+ tsif_detach(tsif_driver->tsif_handler);
+
+ /* release mutex to allow work queue to finish scheduled work */
+ mutex_unlock(&mpq_dmx_tsif_info.tsif[i].mutex);
+ /* flush the work queue and destroy it */
+ flush_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+ destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+
+ mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+ }
+
+ mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tsif_plugin_init);
+module_exit(mpq_dmx_tsif_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSIF HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
new file mode 100644
index 0000000..406ae52
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -0,0 +1,847 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <mach/msm_tspp.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+#define TSIF_COUNT 2
+
+#define TSPP_FILTERS_COUNT 16
+
+/* For each TSIF we allocate two pipes, one for PES and one for sections */
+#define TSPP_PES_CHANNEL 0
+#define TSPP_SECTION_CHANNEL 1
+
+/* the channel_id set to TSPP driver based on TSIF number and channel type */
+#define TSPP_CHANNEL_ID(tsif, ch) ((tsif << 1) + ch)
+#define TSPP_IS_PES_CHANNEL(ch_id) ((ch_id & 0x1) == 0)
+#define TSPP_GET_TSIF_NUM(ch_id) (ch_id >> 1)
+
+/* mask that set to care for all bits in pid filter */
+#define TSPP_PID_MASK 0x1FFF
+
+/* dvb-demux defines pid 0x2000 as full capture pid */
+#define TSPP_PASS_THROUGH_PID 0x2000
+
+/* TODO - NEED TO SET THESE PROPERLY
+ * once TSPP driver is ready, reduce TSPP_BUFFER_SIZE
+ * to single packet and set TSPP_BUFFER_COUNT accordingly
+ */
+
+#define TSPP_RAW_TTS_SIZE 192
+
+/* Size of single descriptor */
+#define TSPP_BUFFER_SIZE (TSPP_RAW_TTS_SIZE * 35)
+
+/* Number of descriptors, total size: TSPP_BUFFER_SIZE*TSPP_BUFFER_COUNT */
+#define TSPP_BUFFER_COUNT (16)
+
+/* When TSPP notifies demux that new packets are received */
+#define TSPP_NOTIFICATION_SIZE (TSPP_RAW_TTS_SIZE * 100)
+
+/* Channel timeout in msec */
+#define TSPP_CHANNEL_TIMEOUT 16
+
+
+/*
+ * Work scheduled each time TSPP notifies dmx
+ * of new TS packet in some channel
+ */
+struct tspp_work {
+ struct work_struct work;
+ int channel_id;
+};
+
+/* The following structure hold singelton information
+ * required for dmx implementation on top of TSPP.
+ */
+static struct
+{
+ /* Information for each TSIF input processing */
+ struct {
+ /*
+ * TSPP pipe holding all TS packets with PES data.
+ * The following is reference count for number of feeds
+ * allocated on that pipe.
+ */
+ int pes_channel_ref;
+
+ /* work used to submit to workqueue to process pes channel */
+ struct tspp_work pes_work;
+
+ /*
+ * TSPP pipe holding all TS packets with section data.
+ * The following is reference count for number of feeds
+ * allocated on that pipe.
+ */
+ int section_channel_ref;
+
+ /* work used to submit to workqueue to process pes channel */
+ struct tspp_work section_work;
+
+ /*
+ * Holds PIDs of allocated TSPP filters along with
+ * how many feeds are opened on same PID.
+ */
+ struct {
+ int pid;
+ int ref_count;
+ } filters[TSPP_FILTERS_COUNT];
+
+ /* workqueue that processes TS packets from specific TSIF */
+ struct workqueue_struct *workqueue;
+
+ /* TSIF alias */
+ char name[TSIF_NAME_LENGTH];
+
+ /* Pointer to the demux connected to this TSIF */
+ struct mpq_demux *mpq_demux;
+
+ /* mutex protecting the data-structure */
+ struct mutex mutex;
+ } tsif[TSIF_COUNT];
+} mpq_dmx_tspp_info;
+
+
+/**
+ * Returns a free filter slot that can be used.
+ *
+ * @tsif: The TSIF to allocate filter from
+ * @channel_id: The channel allocating filter to
+ *
+ * Return filter index or -1 if no filters available
+ *
+ * To give priority to PES data, for pes filters
+ * the table is scanned from high to low priority,
+ * and sections from low to high priority. This way TSPP
+ * would get a match on PES data filters faster as they
+ * are scanned first.
+ */
+static int mpq_tspp_get_free_filter_slot(int tsif, int channel_id)
+{
+ int i;
+
+ if (TSPP_IS_PES_CHANNEL(channel_id)) {
+ for (i = 0; i < TSPP_FILTERS_COUNT; i++)
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+ return i;
+ } else {
+ for (i = TSPP_FILTERS_COUNT-1; i >= 0; i--)
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+ return i;
+ }
+
+ return -ENOMEM;
+}
+
+/**
+ * Returns filter index of specific pid.
+ *
+ * @tsif: The TSIF to which the pid is allocated
+ * @pid: The pid to search for
+ *
+ * Return filter index or -1 if no filter available
+ */
+static int mpq_tspp_get_filter_slot(int tsif, int pid)
+{
+ int i;
+
+ for (i = 0; i < TSPP_FILTERS_COUNT; i++)
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == pid)
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * Worker function that processes the TS packets notified by TSPP.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tspp_work(struct work_struct *worker)
+{
+ struct tspp_work *tspp_work =
+ container_of(worker, struct tspp_work, work);
+ struct mpq_demux *mpq_demux;
+ int channel_id = tspp_work->channel_id;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ const struct tspp_data_descriptor *tspp_data_desc;
+ int ref_count;
+
+ mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+
+ /* Lock against the TSPP filters data-structure */
+ if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+ return;
+
+ /* Make sure channel is still active */
+ if (TSPP_IS_PES_CHANNEL(channel_id))
+ ref_count = mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+ else
+ ref_count = mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+
+ if (ref_count == 0) {
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return;
+ }
+
+ mpq_demux->hw_notification_size = 0;
+
+ /* Go through all filled descriptors and perform demuxing on them */
+ while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
+ mpq_demux->hw_notification_size +=
+ (tspp_data_desc->size / TSPP_RAW_TTS_SIZE);
+
+ dvb_dmx_swfilter_format(
+ &mpq_demux->demux,
+ tspp_data_desc->virt_base,
+ tspp_data_desc->size,
+ DMX_TSP_FORMAT_192_TAIL);
+
+ /* Notify TSPP that the buffer is no longer needed */
+ tspp_release_buffer(0, channel_id, tspp_data_desc->id);
+ }
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+}
+
+/**
+ * Callback function from TSPP when new data is ready.
+ *
+ * @channel_id: Channel with new TS packets
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tspp_callback(u32 channel_id, void *user)
+{
+ int tsif = (int)user;
+ struct work_struct *work;
+ struct mpq_demux *mpq_demux;
+
+ /* Save statistics on TSPP notifications */
+ mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+ mpq_dmx_update_hw_statistics(mpq_demux);
+
+ if (TSPP_IS_PES_CHANNEL(channel_id))
+ work = &mpq_dmx_tspp_info.tsif[tsif].pes_work.work;
+ else
+ work = &mpq_dmx_tspp_info.tsif[tsif].section_work.work;
+
+ /* Scheudle a new work to demux workqueue */
+ if (!work_pending(work))
+ queue_work(mpq_dmx_tspp_info.tsif[tsif].workqueue, work);
+}
+
+/**
+ * Configure TSPP channel to filter the PID of new feed.
+ *
+ * @feed: The feed to configure the channel with
+ *
+ * Return error status
+ *
+ * The function checks if the new PID can be added to an already
+ * allocated channel, if not, a new channel is allocated and configured.
+ */
+static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
+{
+ struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+ enum tspp_source tspp_source;
+ struct tspp_filter tspp_filter;
+ int tsif;
+ int ret;
+ int channel_id;
+ int *channel_ref_count;
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ tspp_source = TSPP_SOURCE_TSIF0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ tspp_source = TSPP_SOURCE_TSIF1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ /*
+ * It is possible that this PID was already requested before.
+ * Can happen if we play and record same PES or PCR
+ * piggypacked on video packet.
+ */
+ ret = mpq_tspp_get_filter_slot(tsif, feed->pid);
+ if (ret >= 0) {
+ /* PID already configured */
+ mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return 0;
+ }
+
+ /* determine to which pipe the feed should be routed: section or pes */
+ if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+ channel_ref_count =
+ &mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+ } else {
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+ channel_ref_count =
+ &mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+ }
+
+ /* check if required TSPP pipe is already allocated or not */
+ if (*channel_ref_count == 0) {
+ ret = tspp_open_channel(0, channel_id);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_open_channel(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_failed;
+ }
+
+ /* set TSPP source */
+ ret = tspp_open_stream(0, channel_id, tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_select_source(%d,%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ tspp_source,
+ ret);
+
+ goto add_channel_close_ch;
+ }
+
+ /* register notification on TS packets */
+ tspp_register_notification(0,
+ channel_id,
+ mpq_tspp_callback,
+ (void *)tsif,
+ TSPP_CHANNEL_TIMEOUT);
+
+ /* TODO: register allocater and provide allocation function
+ * that allocate from continous memory so that we can have
+ * big notification size, smallest descriptor, and still provide
+ * TZ with single big buffer based on notification size.
+ */
+
+ /* set buffer/descriptor size and count */
+ ret = tspp_allocate_buffers(0,
+ channel_id,
+ TSPP_BUFFER_COUNT,
+ TSPP_BUFFER_SIZE,
+ TSPP_NOTIFICATION_SIZE,
+ NULL,
+ NULL);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_allocate_buffers(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_unregister_notif;
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
+ }
+
+ /* add new PID to the existing pipe */
+ ret = mpq_tspp_get_free_filter_slot(tsif, channel_id);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_allocate_filter_slot(%d, %d) failed\n",
+ __func__,
+ tsif,
+ channel_id);
+
+ goto add_channel_unregister_notif;
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].filters[ret].pid = feed->pid;
+ mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+
+ tspp_filter.priority = ret;
+ if (feed->pid == TSPP_PASS_THROUGH_PID) {
+ /* pass all pids */
+ tspp_filter.pid = 0;
+ tspp_filter.mask = 0;
+ } else {
+ tspp_filter.pid = feed->pid;
+ tspp_filter.mask = TSPP_PID_MASK;
+ }
+
+ /*
+ * Include TTS in RAW packets, if you change this to
+ * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+ * accordingly.
+ */
+ tspp_filter.mode = TSPP_MODE_RAW;
+ tspp_filter.source = tspp_source;
+ tspp_filter.decrypt = 0;
+ ret = tspp_add_filter(0, channel_id, &tspp_filter);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_add_filter(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_free_filter_slot;
+ }
+
+ (*channel_ref_count)++;
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return 0;
+
+add_channel_free_filter_slot:
+ mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+ mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+add_channel_unregister_notif:
+ tspp_unregister_notification(0, channel_id);
+add_channel_close_ch:
+ tspp_close_channel(0, channel_id);
+add_channel_failed:
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return ret;
+}
+
+/**
+ * Removes filter from TSPP.
+ *
+ * @feed: The feed to remove
+ *
+ * Return error status
+ *
+ * The function checks if this is the only PID allocated within
+ * the channel, if so, the channel is closed as well.
+ */
+static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
+{
+ int tsif;
+ int ret;
+ int channel_id;
+ int *channel_ref_count;
+ struct tspp_filter tspp_filter;
+ struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ /* determine to which pipe the feed should be routed: section or pes */
+ if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+ channel_ref_count =
+ &mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+ } else {
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+ channel_ref_count =
+ &mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+ }
+
+ /* check if required TSPP pipe is already allocated or not */
+ if (*channel_ref_count == 0) {
+ /* invalid feed provided as the channel is not allocated */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed (%d)\n",
+ __func__,
+ channel_id);
+
+ ret = -EINVAL;
+ goto remove_channel_failed;
+ }
+
+ tspp_filter.priority = mpq_tspp_get_filter_slot(tsif, feed->pid);
+
+ if (tspp_filter.priority < 0) {
+ /* invalid feed provided as it has no filter allocated */
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
+ __func__,
+ feed->pid,
+ tsif);
+
+ ret = -EINVAL;
+ goto remove_channel_failed;
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+
+ if (mpq_dmx_tspp_info.tsif[tsif].
+ filters[tspp_filter.priority].ref_count) {
+ /*
+ * there are still references to this pid, do not
+ * remove the filter yet
+ */
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return 0;
+ }
+
+ ret = tspp_remove_filter(0, channel_id, &tspp_filter);
+ if (ret < 0) {
+ /* invalid feed provided as it has no filter allocated */
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_remove_filter failed (%d,%d)\n",
+ __func__,
+ channel_id,
+ tspp_filter.priority);
+
+ goto remove_channel_failed_restore_count;
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+ (*channel_ref_count)--;
+
+ if (*channel_ref_count == 0) {
+ /* channel is not used any more, release it */
+ tspp_unregister_notification(0, channel_id);
+ tspp_close_channel(0, channel_id);
+ }
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return 0;
+
+remove_channel_failed_restore_count:
+ mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count++;
+
+remove_channel_failed:
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ return ret;
+}
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+ int ret;
+ struct mpq_demux *mpq_demux =
+ (struct mpq_demux *)feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid mpq_demux handle\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* source from TSPP, need to configure tspp pipe */
+ ret = mpq_tspp_dmx_add_channel(feed);
+
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_tspp_dmx_add_channel failed(%d)\n",
+ __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Always feed sections/PES starting from a new one and
+ * do not partial transfer data from older one
+ */
+ feed->pusi_seen = 0;
+
+ /*
+ * For video PES, data is tunneled to the decoder,
+ * initialize tunneling and pes parsing.
+ */
+ if (mpq_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_init_video_feed(feed);
+
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_init_video_feed failed(%d)\n",
+ __func__,
+ ret);
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0)
+ mpq_tspp_dmx_remove_channel(feed);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ /*
+ * For video PES, data is tunneled to the decoder,
+ * terminate tunnel and pes parsing.
+ */
+ if (mpq_dmx_is_video_feed(feed))
+ mpq_dmx_terminate_video_feed(feed);
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* source from TSPP, need to configure tspp pipe */
+ ret = mpq_tspp_dmx_remove_channel(feed);
+ }
+
+ return ret;
+}
+
+static int mpq_tspp_dmx_write_to_decoder(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ size_t len)
+{
+ /*
+ * It is assumed that this function is called once for each
+ * TS packet of the relevant feed.
+ */
+ if (len > TSPP_RAW_TTS_SIZE)
+ MPQ_DVB_DBG_PRINT(
+ "%s: warnning - len larger than one packet\n",
+ __func__);
+
+ if (mpq_dmx_is_video_feed(feed))
+ return mpq_dmx_process_video_packet(feed, buf);
+
+ if (mpq_dmx_is_pcr_feed(feed))
+ return mpq_dmx_process_pcr_packet(feed, buf);
+
+ return 0;
+}
+
+static int mpq_tspp_dmx_init(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *mpq_demux)
+{
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* Set the kernel-demux object capabilities */
+ mpq_demux->demux.dmx.capabilities =
+ DMX_TS_FILTERING |
+ DMX_PES_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING |
+ DMX_CRC_CHECKING |
+ DMX_TS_DESCRAMBLING;
+
+ /* Set dvb-demux "virtual" function pointers */
+ mpq_demux->demux.priv = (void *)mpq_demux;
+ mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+ mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+ mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
+
+ mpq_demux->demux.decoder_fullness_init =
+ mpq_dmx_decoder_fullness_init;
+
+ mpq_demux->demux.decoder_fullness_wait =
+ mpq_dmx_decoder_fullness_wait;
+
+ mpq_demux->demux.decoder_fullness_abort =
+ mpq_dmx_decoder_fullness_abort;
+
+ /* Initialize dvb_demux object */
+ result = dvb_dmx_init(&mpq_demux->demux);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+ goto init_failed;
+ }
+
+ /* Now initailize the dmx-dev object */
+ mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+ mpq_demux->dmxdev.capabilities =
+ DMXDEV_CAP_DUPLEX |
+ DMXDEV_CAP_PULL_MODE |
+ DMXDEV_CAP_PCR_EXTRACTION;
+
+ mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+ result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+ __func__,
+ result);
+ goto init_failed_dmx_release;
+ }
+
+ /* Extend dvb-demux debugfs with TSPP statistics. */
+ mpq_dmx_init_hw_statistics(mpq_demux);
+
+ return 0;
+
+init_failed_dmx_release:
+ dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+ return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+ int i;
+ int j;
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
+
+ mpq_dmx_tspp_info.tsif[i].pes_work.channel_id =
+ TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL);
+
+ INIT_WORK(&mpq_dmx_tspp_info.tsif[i].pes_work.work,
+ mpq_dmx_tspp_work);
+
+ mpq_dmx_tspp_info.tsif[i].section_channel_ref = 0;
+
+ mpq_dmx_tspp_info.tsif[i].section_work.channel_id =
+ TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL);
+
+ INIT_WORK(&mpq_dmx_tspp_info.tsif[i].section_work.work,
+ mpq_dmx_tspp_work);
+
+ for (j = 0; j < TSPP_FILTERS_COUNT; j++) {
+ mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
+ mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
+ }
+
+ snprintf(mpq_dmx_tspp_info.tsif[i].name,
+ TSIF_NAME_LENGTH,
+ "tsif_%d",
+ i);
+
+ mpq_dmx_tspp_info.tsif[i].workqueue =
+ create_singlethread_workqueue(
+ mpq_dmx_tspp_info.tsif[i].name);
+
+ if (mpq_dmx_tspp_info.tsif[i].workqueue == NULL) {
+
+ for (j = 0; j < i; j++) {
+ destroy_workqueue(
+ mpq_dmx_tspp_info.tsif[j].workqueue);
+
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
+ }
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: create_singlethread_workqueue failed\n",
+ __func__);
+
+ return -ENOMEM;
+ }
+
+ mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+
+ ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+ __func__,
+ ret);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+ }
+
+ return ret;
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+ int i;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
+
+ if (mpq_dmx_tspp_info.tsif[i].pes_channel_ref) {
+ tspp_unregister_notification(0, TSPP_PES_CHANNEL);
+ tspp_close_channel(0,
+ TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL));
+ }
+
+ if (mpq_dmx_tspp_info.tsif[i].section_channel_ref) {
+ tspp_unregister_notification(0, TSPP_SECTION_CHANNEL);
+ tspp_close_channel(0,
+ TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL));
+ }
+
+ /* TODO: if we allocate buffer
+ * to TSPP ourself, need to free those as well
+ */
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
+ flush_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+ destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+
+ mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version 1 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
new file mode 100644
index 0000000..d3c2c50
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ /* Always feed sections/PES starting from a new one and
+ * do not partial transfer data from older one
+ */
+ feed->pusi_seen = 0;
+ return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+ MPQ_DVB_DBG_PRINT(
+ "%s(%d) executed\n",
+ __func__,
+ feed->pid);
+
+ return 0;
+}
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return error code
+ */
+static int mpq_tspp_dmx_init(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *mpq_demux)
+{
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* Set the kernel-demux object capabilities */
+ mpq_demux->demux.dmx.capabilities =
+ DMX_TS_FILTERING |
+ DMX_PES_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING |
+ DMX_CRC_CHECKING |
+ DMX_TS_DESCRAMBLING;
+
+ /* Set dvb-demux "virtual" function pointers */
+ mpq_demux->demux.priv = (void *)mpq_demux;
+ mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+ mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+ mpq_demux->demux.write_to_decoder = NULL;
+ mpq_demux->demux.decoder_fullness_init = NULL;
+ mpq_demux->demux.decoder_fullness_wait = NULL;
+ mpq_demux->demux.decoder_fullness_abort = NULL;
+
+ /* Initialize dvb_demux object */
+ result = dvb_dmx_init(&mpq_demux->demux);
+ if (result < 0) {
+ MPQ_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+ goto init_failed;
+ }
+
+ /* Now initailize the dmx-dev object */
+ mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+ mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+ mpq_demux->dmxdev.capabilities =
+ DMXDEV_CAP_DUPLEX |
+ DMXDEV_CAP_PULL_MODE |
+ DMXDEV_CAP_PCR_EXTRACTION;
+
+ mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+ result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+ __func__,
+ result);
+
+ goto init_failed_dmx_release;
+ }
+
+ return 0;
+
+init_failed_dmx_release:
+ dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+ return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ return mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+ mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version2 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/include/mpq_adapter.h b/drivers/media/dvb/mpq/include/mpq_adapter.h
new file mode 100644
index 0000000..c720f91
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_adapter.h
@@ -0,0 +1,146 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_ADAPTER_H
+#define _MPQ_ADAPTER_H
+
+#include "dvbdev.h"
+#include "mpq_stream_buffer.h"
+
+
+
+/** IDs of interfaces holding stream-buffers */
+enum mpq_adapter_stream_if {
+ /** Interface holding stream-buffer for video0 stream */
+ MPQ_ADAPTER_VIDEO0_STREAM_IF = 0,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO1_STREAM_IF = 1,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO2_STREAM_IF = 2,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO3_STREAM_IF = 3,
+
+ /** Maximum number of interfaces holding stream-buffers */
+ MPQ_ADAPTER_MAX_NUM_OF_INTERFACES,
+};
+
+
+/** The meta-data used for video interface */
+struct mpq_adapter_video_meta_data {
+ /**
+ * Indication whether this packet is just a padding packet.
+ * In this case packet should be just disposed along
+ * with the padding in the raw-data buffer.
+ */
+ int is_padding;
+
+ /** Indication whether PTS exist */
+ int pts_exist;
+
+ /** Indication whether DTS exist */
+ int dts_exist;
+
+ /** PTS value associated with the PES data if any */
+ u64 pts;
+
+ /** DTS value associated with the PES data if any */
+ u64 dts;
+} __packed;
+
+
+/** Callback function to notify on registrations of specific interfaces */
+typedef void (*mpq_adapter_stream_if_callback)(
+ enum mpq_adapter_stream_if interface_id,
+ void *user_param);
+
+
+/**
+ * mpq_adapter_get - Returns pointer to Qualcomm DVB adapter
+ *
+ * Return dvb adapter or NULL if not exist.
+ */
+struct dvb_adapter *mpq_adapter_get(void);
+
+
+/**
+ * mpq_adapter_register_stream_if - Register a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The buffer used for the interface
+ *
+ * Return error status
+ *
+ * Stream interface used to connect between two units in tunneling
+ * mode using mpq_streambuffer implementation.
+ * The producer of the interface should register the new interface,
+ * consumer may get the interface using mpq_adapter_get_stream_if.
+ *
+ * Note that the function holds a pointer to this interface,
+ * stream_buffer pointer assumed to be valid as long as interface
+ * is active.
+ */
+int mpq_adapter_register_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer *stream_buffer);
+
+
+/**
+ * mpq_adapter_unregister_stream_if - Un-register a stream interface.
+ *
+ * @interface_id: The interface id
+ *
+ * Return error status
+ */
+int mpq_adapter_unregister_stream_if(
+ enum mpq_adapter_stream_if interface_id);
+
+
+/**
+ * mpq_adapter_get_stream_if - Get buffer used for a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The returned stream buffer
+ *
+ * Return error status
+ */
+int mpq_adapter_get_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer **stream_buffer);
+
+
+/**
+ * mpq_adapter_notify_stream_if - Register notification
+ * to be triggered when a stream interface is registered.
+ *
+ * @interface_id: The interface id
+ * @callback: The callback to be triggered when the interface is registered
+ * @user_param: A parameter that is passed back to the callback function
+ * when triggered.
+ *
+ * Return error status
+ *
+ * Producer may use this to register notification when desired
+ * interface registered in the system and query its information
+ * afterwards using mpq_adapter_get_stream_if.
+ * To remove the callback, this function should be called with NULL
+ * value in callback parameter.
+ */
+int mpq_adapter_notify_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ mpq_adapter_stream_if_callback callback,
+ void *user_param);
+
+#endif /* _MPQ_ADAPTER_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_dvb_debug.h b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
new file mode 100644
index 0000000..4890b85
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DVB_DEBUG_H
+#define _MPQ_DVB_DEBUG_H
+
+/* Enable this line if you want to output debug printouts */
+#define MPG_DVB_DEBUG_ENABLE
+
+#undef MPQ_DVB_DBG_PRINT /* undef it, just in case */
+
+#ifdef MPG_DVB_DEBUG_ENABLE
+#define MPQ_DVB_DBG_PRINT(fmt, args...) pr_debug(fmt, ## args)
+#define MPQ_DVB_ERR_PRINT(fmt, args...) pr_err(fmt, ## args)
+#else /* MPG_DVB_DEBUG_ENABLE */
+#define MPQ_DVB_DBG_PRINT(fmt, args...)
+#define MPQ_DVB_ERR_PRINT(fmt, args...)
+#endif /* MPG_DVB_DEBUG_ENABLE */
+
+
+/*
+ * The following can be used to disable specific printout
+ * by adding a letter to the end of MPQ_DVB_DBG_PRINT
+ */
+#undef MPQ_DVB_DBG_PRINTT
+#define MPQ_DVB_DBG_PRINTT(fmt, args...)
+
+#endif /* _MPQ_DVB_DEBUG_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_stream_buffer.h b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
new file mode 100644
index 0000000..4ea4222
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
@@ -0,0 +1,269 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_STREAM_BUFFER_H
+#define _MPQ_STREAM_BUFFER_H
+
+#include "dvb_ringbuffer.h"
+
+
+/**
+ * DOC: MPQ Stream Buffer
+ *
+ * A stream buffer implmenetation used to transfer data between two units
+ * such as demux and decoders. The implementation relies on dvb_ringbuffer
+ * implementation. Refer to dvb_ringbuffer.h for details.
+ *
+ * The implementation uses two dvb_ringbuffers, one to pass the
+ * raw-data (PES payload for example) and the other to pass
+ * meta-data (information from PES header for example).
+ *
+ * The meta-data uses dvb_ringbuffer packet interface. Each meta-data
+ * packet hold the address and size of raw-data described by the
+ * meta-data packet, in addition to user's own parameters if any required.
+ *
+ * Contrary to dvb_ringbuffer implementation, this API makes sure there's
+ * enough data to read/write when making read/write operations.
+ * Users interested to flush/reset specific buffer, check for bytes
+ * ready or space available for write should use the respective services
+ * in dvb_ringbuffer (dvb_ringbuffer_avail, dvb_ringbuffer_free,
+ * dvb_ringbuffer_reset, dvb_ringbuffer_flush,
+ * dvb_ringbuffer_flush_spinlock_wakeup).
+ *
+ * Concurrency protection is handled in the same manner as in
+ * dvb_ringbuffer implementation.
+ *
+ * Typical call flow from producer:
+ *
+ * - Start writting the raw-data of new packet, the following call is
+ * repeated until end of data of the specific packet
+ *
+ * mpq_streambuffer_data_write(...)
+ *
+ * - Now write a new packet describing the new available raw-data
+ * mpq_streambuffer_pkt_write(...)
+ *
+ * Typical call flow from consumer:
+ *
+ * - Poll for next available packet:
+ * mpq_streambuffer_pkt_next(&streambuff,-1)
+ *
+ * In different approach, consumer can wait on event for new data and then
+ * call mpq_streambuffer_pkt_next, waiting for data can be done as follows:
+ *
+ * wait_event_interruptible(
+ * streambuff->packet_data->queue,
+ * !dvb_ringbuffer_empty(&streambuff->packet_data) ||
+ * (streambuff->packet_data.error != 0);
+ *
+ * - Get the new packet information:
+ * mpq_streambuffer_pkt_read(..)
+ *
+ * - Read the raw-data of the new packet. Here you can use two methods:
+ *
+ * 1. Read the data to a user supplied buffer:
+ * mpq_streambuffer_data_read()
+ *
+ * In this case memory copy is done, read pointer is updated in the raw
+ * data buffer, the amount of raw-data is provided part of the
+ * packet's information. User should then call mpq_streambuffer_pkt_dispose
+ * with dispose_data set to 0 as the raw-data was already disposed.
+ *
+ * 2. Access the data directly using the raw-data address. The address
+ * of the raw data is provided part of the packet's information. User
+ * then should call mpq_streambuffer_pkt_dispose with dispose_data set
+ * to 1 to dispose the packet along with it's raw-data.
+ */
+
+/**
+ * struct mpq_streambuffer - mpq stream buffer representation
+ *
+ * @raw_data: The buffer used to hold the raw-data
+ * @packet_data: The buffer user to hold the meta-data
+ */
+struct mpq_streambuffer {
+ struct dvb_ringbuffer raw_data;
+ struct dvb_ringbuffer packet_data;
+};
+
+/**
+ * struct mpq_streambuffer_packet_header - packet header saved in packet buffer
+ * @user_data_len: length of private user (meta) data
+ * @raw_data_addr: raw-data address in the raw-buffer described by the packet
+ * @raw_data_len: size of raw-data in the raw-data buffer (can be 0)
+ *
+ * The packet structure that is saved in each packet-buffer:
+ * user_data_len
+ * raw_data_addr
+ * raw_data_len
+ * private user-data bytes
+ */
+struct mpq_streambuffer_packet_header {
+ u32 user_data_len;
+ u32 raw_data_addr;
+ u32 raw_data_len;
+} __packed;
+
+/**
+ * mpq_streambuffer_init - Initialize a new stream buffer
+ *
+ * @sbuff: The buffer to initialize
+ * @data_buff: The buffer holding raw-data
+ * @data_buff_len: Size of raw-data buffer
+ * @packet_buff: The buffer holding meta-data
+ * @packet_buff_size: Size of meta-data buffer
+ */
+void mpq_streambuffer_init(
+ struct mpq_streambuffer *sbuff,
+ void *data_buff, size_t data_buff_len,
+ void *packet_buff, size_t packet_buff_size);
+
+/**
+ * mpq_streambuffer_packet_next - Returns index of next avaialble packet.
+ *
+ * @sbuff: The stream buffer
+ * @idx: Previous packet index or -1 to return index of the the first
+ * available packet.
+ * @pktlen: The length of the ready packet
+ *
+ * Return index to the packet-buffer, -1 if buffer is empty
+ *
+ * After getting the index, the user of this function can either
+ * access the packet buffer directly using the returned index
+ * or ask to read the data back from the buffer using mpq_ringbuffer_pkt_read
+ */
+ssize_t mpq_streambuffer_pkt_next(
+ struct mpq_streambuffer *sbuff,
+ ssize_t idx, size_t *pktlen);
+
+/**
+ * mpq_streambuffer_pkt_read - Reads out the packet from the provided index.
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be read
+ * @packet: The read packet's header
+ * @user_data: The read private user data
+ *
+ * Return The actual number of bytes read, -EINVAL if the packet is
+ * already disposed or the packet-data is invalid.
+ *
+ * The packet is not disposed after this function is called, to dispose it
+ * along with the raw-data it points to use mpq_streambuffer_pkt_dispose.
+ * If there are no private user-data, the user-data pointer can be NULL.
+ * The caller of this function must make sure that the private user-data
+ * buffer has enough space for the private user-data length
+ */
+ssize_t mpq_streambuffer_pkt_read(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data);
+
+/**
+ * mpq_streambuffer_pkt_dispose - Disposes a packet from the packet buffer
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be disposed
+ * @dispose_data: Indicates whether to update the read pointer inside the
+ * raw-data buffer for the respective data pointed by the packet.
+ *
+ * Return error status, -EINVAL if the packet-data is invalid
+ *
+ * The function updates the read pointer inside the raw-data buffer
+ * for the respective data pointed by the packet if dispose_data is set.
+ */
+int mpq_streambuffer_pkt_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ int dispose_data);
+
+/**
+ * mpq_streambuffer_pkt_write - Write a new packet to the packet buffer.
+ *
+ * @sbuff: The stream buffer
+ * @packet: The packet header to write
+ * @user_data: The private user-data to be written
+ *
+ * Return error status, -ENOSPC if there's no space to write the packet
+ */
+int mpq_streambuffer_pkt_write(
+ struct mpq_streambuffer *sbuff,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data);
+
+/**
+ * mpq_streambuffer_data_write - Write data to raw-data buffer
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer holding the data to be written
+ * @len: The length of the data buffer
+ *
+ * Return The actual number of bytes written or -ENOSPC if
+ * no space to write the data
+ */
+ssize_t mpq_streambuffer_data_write(
+ struct mpq_streambuffer *sbuff,
+ const u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_write_deposit - Advances the raw-buffer write pointer.
+ * Assumes the raw-data was written by the user directly
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data that was already written
+ *
+ * Return error status
+ */
+int mpq_streambuffer_data_write_deposit(
+ struct mpq_streambuffer *sbuff,
+ size_t len);
+
+/**
+ * mpq_streambuffer_data_read - Reads out raw-data to the provided buffer.
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer to read the raw-data data to
+ * @len: The length of the buffer that will hold the raw-data
+ *
+ * Return The actual number of bytes read
+ *
+ * This fucntion copies the data from the ring-buffer to the
+ * provided buf parameter. The user can save the extra copy by accessing
+ * the data pointer directly and reading from it, then update the
+ * read pointer by the amount of data that was read using
+ * mpq_streambuffer_data_read_dispose
+ */
+size_t mpq_streambuffer_data_read(
+ struct mpq_streambuffer *sbuff,
+ u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer.
+ * Assumes the raw-data was read by the user directly.
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data to be disposed
+ *
+ * Return error status, -EINVAL if buffer there's no enough data to
+ * be disposed
+ *
+ * The user can instead dipose a packet along with the data in the
+ * raw-data buffer using mpq_streambuffer_pkt_dispose.
+ */
+int mpq_streambuffer_data_read_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t len);
+
+
+
+#endif /* _MPQ_STREAM_BUFFER_H */
+
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index eee351b..3416c91 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -87,7 +87,7 @@
rcdev->input_name = GPIO_IR_DEVICE_NAME;
rcdev->input_id.bustype = BUS_HOST;
rcdev->driver_name = GPIO_IR_DRIVER_NAME;
- rcdev->map_name = RC_MAP_RC6_PHILIPS;
+ rcdev->map_name = RC_MAP_SAMSUNG_NECX;
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = pdata->gpio_nr;
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.c b/drivers/media/video/msm/io/msm_camera_i2c.c
index a3cc012..6f45637 100644
--- a/drivers/media/video/msm/io/msm_camera_i2c.c
+++ b/drivers/media/video/msm/io/msm_camera_i2c.c
@@ -252,7 +252,7 @@
enum msm_camera_i2c_data_type dt;
if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
rc = msm_camera_i2c_poll(client, reg_conf_tbl->reg_addr,
- reg_conf_tbl->reg_addr, reg_conf_tbl->dt);
+ reg_conf_tbl->reg_data, reg_conf_tbl->dt);
} else {
if (reg_conf_tbl->dt == 0)
dt = data_type;
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index e458429..3b115ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1021,11 +1021,8 @@
if (/*interrupt*/0)
*c |= MCI_CPSM_INTERRUPT;
- if (cmd->opcode == MMC_READ_SINGLE_BLOCK ||
- cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
- cmd->opcode == MMC_WRITE_BLOCK ||
- cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
- cmd->opcode == SD_IO_RW_EXTENDED)
+ /* DAT_CMD bit should be set for all ADTC */
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
*c |= MCI_CSPM_DATCMD;
/* Check if AUTO CMD19 is required or not? */
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 053b81f..c040fac 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -19,6 +19,7 @@
#include <linux/usb/msm_hsusb.h>
#include <mach/usb_bam.h>
#include <mach/sps.h>
+#include <linux/workqueue.h>
#define USB_SUMMING_THRESHOLD 512
#define CONNECTIONS_NUM 4
@@ -29,11 +30,20 @@
static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2];
static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2];
static struct platform_device *usb_bam_pdev;
+static struct workqueue_struct *usb_bam_wq;
+
+struct usb_bam_wake_event_info {
+ struct sps_register_event event;
+ int (*callback)(void *);
+ void *param;
+ struct work_struct wake_w;
+};
struct usb_bam_connect_info {
u8 idx;
u8 *src_pipe;
u8 *dst_pipe;
+ struct usb_bam_wake_event_info peer_event;
bool enabled;
};
@@ -48,7 +58,7 @@
u8 *usb_pipe_idx)
{
int ret;
- struct sps_pipe *pipe = sps_pipes[connection_idx][pipe_dir];
+ struct sps_pipe **pipe = &sps_pipes[connection_idx][pipe_dir];
struct sps_connect *connection =
&sps_connections[connection_idx][pipe_dir];
struct msm_usb_bam_platform_data *pdata =
@@ -58,13 +68,13 @@
(struct usb_bam_pipe_connect *)(pdata->connections +
bam_offset(pdata) + (2*connection_idx+pipe_dir));
- pipe = sps_alloc_endpoint();
- if (pipe == NULL) {
+ *pipe = sps_alloc_endpoint();
+ if (*pipe == NULL) {
pr_err("%s: sps_alloc_endpoint failed\n", __func__);
return -ENOMEM;
}
- ret = sps_get_config(pipe, connection);
+ ret = sps_get_config(*pipe, connection);
if (ret) {
pr_err("%s: tx get config failed %d\n", __func__, ret);
goto get_config_failed;
@@ -114,7 +124,7 @@
connection->desc = desc_mem_buf[connection_idx][pipe_dir];
connection->event_thresh = 512;
- ret = sps_connect(pipe, connection);
+ ret = sps_connect(*pipe, connection);
if (ret < 0) {
pr_err("%s: tx connect error %d\n", __func__, ret);
goto error;
@@ -122,10 +132,10 @@
return 0;
error:
- sps_disconnect(pipe);
+ sps_disconnect(*pipe);
fifo_setup_error:
get_config_failed:
- sps_free_endpoint(pipe);
+ sps_free_endpoint(*pipe);
return ret;
}
@@ -168,6 +178,58 @@
return 0;
}
+static void usb_bam_wake_work(struct work_struct *w)
+{
+ struct usb_bam_wake_event_info *wake_event_info =
+ container_of(w, struct usb_bam_wake_event_info, wake_w);
+
+ wake_event_info->callback(wake_event_info->param);
+}
+
+static void usb_bam_wake_cb(struct sps_event_notify *notify)
+{
+ struct usb_bam_wake_event_info *wake_event_info =
+ (struct usb_bam_wake_event_info *)notify->user;
+
+ queue_work(usb_bam_wq, &wake_event_info->wake_w);
+}
+
+int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *user), void* param)
+{
+ struct sps_pipe *pipe = sps_pipes[idx][PEER_PERIPHERAL_TO_USB];
+ struct sps_connect *sps_connection =
+ &sps_connections[idx][PEER_PERIPHERAL_TO_USB];
+ struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+ struct usb_bam_wake_event_info *wake_event_info =
+ &connection->peer_event;
+ int ret;
+
+ wake_event_info->param = param;
+ wake_event_info->callback = callback;
+ wake_event_info->event.mode = SPS_TRIGGER_CALLBACK;
+ wake_event_info->event.xfer_done = NULL;
+ wake_event_info->event.callback = callback ? usb_bam_wake_cb : NULL;
+ wake_event_info->event.user = wake_event_info;
+ wake_event_info->event.options = SPS_O_WAKEUP;
+ ret = sps_register_event(pipe, &wake_event_info->event);
+ if (ret) {
+ pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ sps_connection->options = callback ?
+ (SPS_O_AUTO_ENABLE | SPS_O_WAKEUP | SPS_O_WAKEUP_IS_ONESHOT) :
+ SPS_O_AUTO_ENABLE;
+ ret = sps_set_config(pipe, sps_connection);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int usb_bam_init(void)
{
u32 h_usb;
@@ -275,8 +337,11 @@
dev_dbg(&pdev->dev, "usb_bam_probe\n");
- for (i = 0; i < CONNECTIONS_NUM; i++)
+ for (i = 0; i < CONNECTIONS_NUM; i++) {
usb_bam_connections[i].enabled = 0;
+ INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
+ usb_bam_wake_work);
+ }
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "missing platform_data\n");
@@ -288,11 +353,26 @@
if (ret)
dev_err(&pdev->dev, "failed to create device file\n");
+ usb_bam_wq = alloc_workqueue("usb_bam_wq",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (!usb_bam_wq) {
+ pr_err("unable to create workqueue usb_bam_wq\n");
+ return -ENOMEM;
+ }
+
return ret;
}
+static int usb_bam_remove(struct platform_device *pdev)
+{
+ destroy_workqueue(usb_bam_wq);
+
+ return 0;
+}
+
static struct platform_driver usb_bam_driver = {
.probe = usb_bam_probe,
+ .remove = usb_bam_remove,
.driver = { .name = "usb_bam", },
};
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
index eeacceb..39d4720 100644
--- a/drivers/usb/gadget/ci13xxx_msm_hsic.c
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -65,6 +65,7 @@
struct msm_xo_voter *xo_handle;
struct workqueue_struct *wq;
struct work_struct suspend_w;
+ struct msm_hsic_peripheral_platform_data *pdata;
};
static int msm_hsic_init_vddcx(struct msm_hsic_per *mhsic, int init)
@@ -365,8 +366,10 @@
*/
mb();
- clk_disable(mhsic->iface_clk);
- clk_disable(mhsic->core_clk);
+ if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+ clk_disable(mhsic->iface_clk);
+ clk_disable(mhsic->core_clk);
+ }
clk_disable(mhsic->phy_clk);
clk_disable(mhsic->cal_clk);
@@ -416,8 +419,10 @@
dev_err(mhsic->dev, "%s failed to vote for TCXO %d\n",
__func__, ret);
- clk_enable(mhsic->iface_clk);
- clk_enable(mhsic->core_clk);
+ if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+ clk_enable(mhsic->iface_clk);
+ clk_enable(mhsic->core_clk);
+ }
clk_enable(mhsic->phy_clk);
clk_enable(mhsic->cal_clk);
@@ -611,9 +616,17 @@
struct resource *res;
struct msm_hsic_per *mhsic;
int ret = 0;
+ struct msm_hsic_peripheral_platform_data *pdata;
dev_dbg(&pdev->dev, "msm-hsic probe\n");
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+ return -ENODEV;
+ } else {
+ pdata = pdev->dev.platform_data;
+ }
+
mhsic = kzalloc(sizeof(struct msm_hsic_per), GFP_KERNEL);
if (!mhsic) {
dev_err(&pdev->dev, "unable to allocate msm_hsic\n");
@@ -622,6 +635,7 @@
the_mhsic = mhsic;
platform_set_drvdata(pdev, mhsic);
mhsic->dev = &pdev->dev;
+ mhsic->pdata = pdata;
mhsic->irq = platform_get_irq(pdev, 0);
if (mhsic->irq < 0) {
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 958ed31..fcbc75c 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -414,6 +414,60 @@
kfree(f->name);
}
+static void frmnet_suspend(struct usb_function *f)
+{
+ struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned port_num;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
+
+ pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(dxport),
+ dev, dev->port_num);
+
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ break;
+ case USB_GADGET_XPORT_BAM2BAM:
+ gbam_suspend(&dev->port, port_num, dxport);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ }
+}
+
+static void frmnet_resume(struct usb_function *f)
+{
+ struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned port_num;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
+
+ pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(dxport),
+ dev, dev->port_num);
+
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ break;
+ case USB_GADGET_XPORT_BAM2BAM:
+ gbam_resume(&dev->port, port_num, dxport);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ }
+}
+
static void frmnet_disable(struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
@@ -912,6 +966,8 @@
f->disable = frmnet_disable;
f->set_alt = frmnet_set_alt;
f->setup = frmnet_setup;
+ f->suspend = frmnet_suspend;
+ f->resume = frmnet_resume;
dev->port.send_cpkt_response = frmnet_send_cpkt_response;
dev->port.disconnect = frmnet_disconnect;
dev->port.connect = frmnet_connect;
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 3113c45..d379c66 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -1214,3 +1214,49 @@
return ret;
}
+
+static int gbam_wake_cb(void *param)
+{
+ struct gbam_port *port = (struct gbam_port *)param;
+ struct bam_ch_info *d;
+ struct f_rmnet *dev;
+
+ dev = port_to_rmnet(port->gr);
+ d = &port->data_ch;
+
+ pr_debug("%s: woken up by peer\n", __func__);
+
+ return usb_gadget_wakeup(dev->cdev->gadget);
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM)
+ return;
+
+ port = bam2bam_ports[port_num];
+ d = &port->data_ch;
+
+ pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+ usb_bam_register_wake_cb(d->connection_idx, gbam_wake_cb, port);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM)
+ return;
+
+ port = bam2bam_ports[port_num];
+ d = &port->data_ch;
+
+ pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+ usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
+}
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 386101c..0f7c4fb 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -50,6 +50,8 @@
int gbam_connect(struct grmnet *gr, u8 port_num,
enum transport_type trans, u8 connection_idx);
void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
int gsmd_ctrl_setup(unsigned int count);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index 2862b00..6d3a05a 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -655,8 +655,9 @@
ptr = ddl_pmem_alloc(&dec_bufs->context, buf_size.sz_context,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
- msm_ion_do_cache_op(ddl_context->video_ion_client,
+ goto fail_free_exit;
+ else
+ msm_ion_do_cache_op(ddl_context->video_ion_client,
dec_bufs->context.alloc_handle,
dec_bufs->context.virtual_base_addr,
dec_bufs->context.buffer_size,
@@ -667,77 +668,77 @@
ptr = ddl_pmem_alloc(&dec_bufs->h264_nb_ip, buf_size.sz_nb_ip,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_vert_nb_mv > 0) {
dec_bufs->h264_vert_nb_mv.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->h264_vert_nb_mv,
buf_size.sz_vert_nb_mv, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_nb_dcac > 0) {
dec_bufs->nb_dcac.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->nb_dcac, buf_size.sz_nb_dcac,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_upnb_mv > 0) {
dec_bufs->upnb_mv.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->upnb_mv, buf_size.sz_upnb_mv,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_sub_anchor_mv > 0) {
dec_bufs->sub_anchor_mv.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->sub_anchor_mv,
buf_size.sz_sub_anchor_mv, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_overlap_xform > 0) {
dec_bufs->overlay_xform.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->overlay_xform,
buf_size.sz_overlap_xform, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_bit_plane3 > 0) {
dec_bufs->bit_plane3.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->bit_plane3,
buf_size.sz_bit_plane3, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_bit_plane2 > 0) {
dec_bufs->bit_plane2.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->bit_plane2,
buf_size.sz_bit_plane2, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_bit_plane1 > 0) {
dec_bufs->bit_plane1.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->bit_plane1,
buf_size.sz_bit_plane1, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_stx_parser > 0) {
dec_bufs->stx_parser.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->stx_parser,
buf_size.sz_stx_parser, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
}
if (buf_size.sz_desc > 0) {
dec_bufs->desc.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&dec_bufs->desc, buf_size.sz_desc,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_free_exit;
else {
if (!res_trk_check_for_sec_session()) {
memset(dec_bufs->desc.align_virtual_addr,
@@ -751,8 +752,10 @@
}
}
}
- if (status)
- ddl_free_dec_hw_buffers(ddl);
+ return status;
+fail_free_exit:
+ status = VCD_ERR_ALLOC_FAIL;
+ ddl_free_dec_hw_buffers(ddl);
return status;
}
@@ -880,66 +883,70 @@
ptr = ddl_pmem_alloc(&enc_bufs->mv, buf_size.sz_mv,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_col_zero > 0) {
enc_bufs->col_zero.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->col_zero,
buf_size.sz_col_zero, DDL_KILO_BYTE(2));
- if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ if (!ptr)
+ goto fail_enc_free_exit;
}
if (buf_size.sz_md > 0) {
enc_bufs->md.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->md, buf_size.sz_md,
DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_pred > 0) {
enc_bufs->pred.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->pred,
buf_size.sz_pred, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_nbor_info > 0) {
enc_bufs->nbor_info.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->nbor_info,
buf_size.sz_nbor_info, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_acdc_coef > 0) {
enc_bufs->acdc_coef.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->acdc_coef,
buf_size.sz_acdc_coef, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_mb_info > 0) {
enc_bufs->mb_info.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->mb_info,
buf_size.sz_mb_info, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
+ goto fail_enc_free_exit;
}
if (buf_size.sz_context > 0) {
enc_bufs->context.mem_type = DDL_MM_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->context,
buf_size.sz_context, DDL_KILO_BYTE(2));
if (!ptr)
- status = VCD_ERR_ALLOC_FAIL;
- msm_ion_do_cache_op(ddl_context->video_ion_client,
+ goto fail_enc_free_exit;
+ else
+ msm_ion_do_cache_op(
+ ddl_context->video_ion_client,
enc_bufs->context.alloc_handle,
enc_bufs->context.virtual_base_addr,
enc_bufs->context.buffer_size,
ION_IOC_CLEAN_INV_CACHES);
}
- if (status)
- ddl_free_enc_hw_buffers(ddl);
}
return status;
+fail_enc_free_exit:
+ status = VCD_ERR_ALLOC_FAIL;
+ ddl_free_enc_hw_buffers(ddl);
+ return status;
}
void ddl_decoder_chroma_dpb_change(struct ddl_client_context *ddl)
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 7822717..11177b8 100644
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -347,7 +347,7 @@
ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
pmem_fd, kernel_vaddr, buffer_index,
&buff_handle);
- if (ion_flag == CACHED) {
+ if (ion_flag == CACHED && buff_handle) {
msm_ion_do_cache_op(client_ctx->user_ion_client,
buff_handle,
(unsigned long *) kernel_vaddr,
@@ -1270,7 +1270,7 @@
kernel_vaddr,
buffer_index,
&buff_handle);
- if (ion_flag == CACHED) {
+ if (ion_flag == CACHED && buff_handle) {
msm_ion_do_cache_op(client_ctx->user_ion_client,
buff_handle,
(unsigned long *)kernel_vaddr,
diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c
index 5542856..1b77b67 100644
--- a/drivers/video/msm/vidc/common/enc/venc.c
+++ b/drivers/video/msm/vidc/common/enc/venc.c
@@ -265,7 +265,7 @@
ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
pmem_fd, kernel_vaddr, buffer_index,
&buff_handle);
- if (ion_flag == CACHED) {
+ if (ion_flag == CACHED && buff_handle) {
msm_ion_do_cache_op(client_ctx->user_ion_client,
buff_handle,
(unsigned long *) kernel_vaddr,
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.c b/drivers/video/msm/vidc/common/enc/venc_internal.c
index ad61c6e..bbbe0cf 100644
--- a/drivers/video/msm/vidc/common/enc/venc_internal.c
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.c
@@ -1688,7 +1688,7 @@
&buff_handle);
if (vcd_input_buffer.data_len > 0) {
- if (ion_flag == CACHED) {
+ if (ion_flag == CACHED && buff_handle) {
msm_ion_do_cache_op(
client_ctx->user_ion_client,
buff_handle,
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index f078f3a..7f963e6 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -5,6 +5,8 @@
* & Ralph Metzler <ralph@convergence.de>
* for convergence integrated media GmbH
*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -119,6 +121,29 @@
__u32 flags;
};
+struct dmx_buffer_status {
+ /* size of buffer in bytes */
+ unsigned int size;
+
+ /* fullness of buffer in bytes */
+ unsigned int fullness;
+
+ /*
+ * How many bytes are free
+ * It's the same as: size-fullness-1
+ */
+ unsigned int free_bytes;
+
+ /* read pointer offset in bytes */
+ unsigned int read_offset;
+
+ /* write pointer offset in bytes */
+ unsigned int write_offset;
+
+ /* non-zero if data error occured */
+ int error;
+};
+
typedef struct dmx_caps {
__u32 caps;
int num_decoders;
@@ -135,6 +160,34 @@
DMX_SOURCE_DVR3
} dmx_source_t;
+enum dmx_tsp_format_t {
+ DMX_TSP_FORMAT_188 = 0,
+ DMX_TSP_FORMAT_192_TAIL,
+ DMX_TSP_FORMAT_192_HEAD,
+ DMX_TSP_FORMAT_204,
+};
+
+enum dmx_playback_mode_t {
+ /*
+ * In push mode, if one of output buffers
+ * is full, the buffer would overflow
+ * and demux continue processing incoming stream.
+ * This is the default mode. When playing from frontend,
+ * this is the only mode that is allowed.
+ */
+ DMX_PB_MODE_PUSH = 0,
+
+ /*
+ * In pull mode, if one of output buffers
+ * is full, demux stalls waiting for free space,
+ * this would cause DVR input buffer fullness
+ * to accumulate.
+ * This mode is possible only when playing
+ * from DVR.
+ */
+ DMX_PB_MODE_PULL,
+};
+
struct dmx_stc {
unsigned int num; /* input : which STC? 0..N */
unsigned int base; /* output: divisor for stc to get 90 kHz clock */
@@ -153,5 +206,12 @@
#define DMX_GET_STC _IOWR('o', 50, struct dmx_stc)
#define DMX_ADD_PID _IOW('o', 51, __u16)
#define DMX_REMOVE_PID _IOW('o', 52, __u16)
+#define DMX_SET_TS_PACKET_FORMAT _IOW('o', 53, enum dmx_tsp_format_t)
+#define DMX_SET_TS_OUT_FORMAT _IOW('o', 54, enum dmx_tsp_format_t)
+#define DMX_SET_DECODER_BUFFER_SIZE _IO('o', 55)
+#define DMX_GET_BUFFER_STATUS _IOR('o', 56, struct dmx_buffer_status)
+#define DMX_RELEASE_DATA _IO('o', 57)
+#define DMX_FEED_DATA _IO('o', 58)
+#define DMX_SET_PLAYBACK_MODE _IOW('o', 59, enum dmx_playback_mode_t)
#endif /*_DVBDMX_H_*/
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 14d49eb..b53d9dd 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -159,19 +159,6 @@
};
/**
- * SPS Pipes direction.
- *
- * USB_TO_PEER_PERIPHERAL USB (as Producer) to other
- * peer peripheral.
- * PEER_PERIPHERAL_TO_USB Other Peripheral to
- * USB (as consumer).
- */
-enum usb_bam_pipe_dir {
- USB_TO_PEER_PERIPHERAL,
- PEER_PERIPHERAL_TO_USB,
-};
-
-/**
* struct msm_otg_platform_data - platform device data
* for msm_otg driver.
* @phy_init_seq: PHY configuration sequence. val, reg pairs
@@ -355,6 +342,10 @@
unsigned int dock_connect_irq;
};
+struct msm_hsic_peripheral_platform_data {
+ bool keep_core_clk_on_suspend_workaround;
+};
+
struct usb_bam_pipe_connect {
u32 src_phy_addr;
int src_pipe_index;