Merge "arm/dt: msm8226: Update number of sensors"
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index cd14056..f97e063 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -31,6 +31,14 @@
request by different video encoder usecases.
- qcom,dec-ddr-ab-ib : list of bus vectors(ab, ib pair) for ddr bandwidth
request by different video decoder usecases.
+- qcom,iommu-groups : list of IOMMU groups to be used. Groups are defined as
+ phandles in <target>-iommu-domains.dtsi (e.g msm8974-v1-iommu-domains.dtsi)
+- qcom,iommu-group-buffer-types : bitmap of buffer types that can be mapped into
+ the corresponding IOMMU group. Buffer types are defined within the vidc driver
+ by "enum hal_buffer" in msm_smem.h
+- qcom,buffer-type-tz-usage-table : a key-value pair, mapping a buffer type
+ (enum hal_buffer) to its corresponding TZ usage. The TZ usages are defined
+ as "enum cp_mem_usage" in include/linux/msm_ion.h
Example:
@@ -59,4 +67,8 @@
<60000 664950>;
qcom,dec-ddr-ab-ib = <0 0>,
<110000 909000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_cp>;
+ qcom,iommu-group-buffer-types = <0xfff 0x1ff>;
+ qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+ <0x1fe 0x2>;
};
diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt
index 74e0476..e98ee05 100644
--- a/Documentation/devicetree/bindings/memory.txt
+++ b/Documentation/devicetree/bindings/memory.txt
@@ -36,6 +36,7 @@
reg = <(baseaddr) (size)>;
(linux,contiguous-region);
(linux,default-contiguous-region);
+ label = (unique_name);
};
name: an name given to the defined region.
@@ -47,7 +48,11 @@
linux,default-contiguous-region: property indicating that the region
is the default region for all contiguous memory
allocations, Linux specific (optional)
-
+label: an internal name used for automatically associating the
+ cma region with a given device. The label is optional;
+ if the label is not given the client is responsible for
+ calling the appropriate functions to associate the region
+ with a device.
* Device nodes
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 9d0b0a5..2cdc7ff 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -52,6 +52,13 @@
- qcom,hsusb-otg-clk-always-on-workaround: If present then USB core clocks
remain active upon receiving bus suspend and USB cable is connected.
Used for allowing USB to respond for remote wakup.
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "HSUSB_VDDCX" (when voting for VDDCX) or
+ "hsusb_vdd_dig" (when voting for VDDCX Corner voltage),
+ "HSUSB_1p8-supply" and "HSUSB_3p3-supply".
+- qcom,vdd-voltage-level: This property must be a list of three integer
+ values (no, min, max) where each value represents either a voltage
+ in microvolts or a value corresponding to voltage corner.
Example HSUSB OTG controller device node :
usb@f9690000 {
@@ -72,6 +79,10 @@
qcom,hsusb-otg-pmic-id-irq = <47>
qcom,hsusb-otg-lpm-on-dev-suspend;
qcom,hsusb-otg-clk-always-on-workaround;
+ hsusb_vdd_dig-supply = <&pm8226_s1_corner>;
+ HSUSB_1p8-supply = <&pm8226_l10>;
+ HSUSB_3p3-supply = <&pm8226_l20>;
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,msm_bus,name = "usb2";
qcom,msm_bus,num_cases = <2>;
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index de23f4c..6a07bad 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -22,6 +22,33 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>;
+ interrupt-names = "kpdpwr", "resin", "resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,support-reset = <1>;
+ qcom,pull-up = <1>;
+ qcom,s1-timer = <0>;
+ qcom,s2-timer = <2000>;
+ qcom,s2-type = <1>;
+ linux,code = <114>;
+ };
+ };
+
pm8226_gpios: gpios {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index c255781..a7ea350 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -85,9 +85,10 @@
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0>, <0 140 0>;
interrupt-names = "core_irq", "async_irq";
- HSUSB_VDDCX-supply = <&pm8226_s1>;
+ hsusb_vdd_dig-supply = <&pm8226_s1_corner>;
HSUSB_1p8-supply = <&pm8226_l10>;
HSUSB_3p3-supply = <&pm8226_l20>;
+ qcom,vdd-voltage-level = <1 5 7>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index f55cff2..dfa22c1 100644
--- a/arch/arm/boot/dts/msm8974-ion.dtsi
+++ b/arch/arm/boot/dts/msm8974-ion.dtsi
@@ -24,8 +24,7 @@
compatible = "qcom,msm-ion-reserve";
reg = <8>;
qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x7800000>;
+ linux,contiguous-region = <&secure_mem>;
};
qcom,ion-heap@25 { /* IOMMU HEAP */
diff --git a/arch/arm/boot/dts/msm8974-v1.dtsi b/arch/arm/boot/dts/msm8974-v1.dtsi
index fc3a1d3..64014b3 100644
--- a/arch/arm/boot/dts/msm8974-v1.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -110,6 +110,10 @@
<1010000 1818000>,
<1616000 2908800>,
<2020000 6400000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_cp>;
+ qcom,iommu-group-buffer-types = <0xfff 0x1ff>;
+ qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+ <0x1fe 0x2>;
};
&sfpb_spinlock {
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 7e6c0bf..3dda20f 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -110,6 +110,12 @@
<1620000 970000>,
<2024000 1212000>,
<2132000 1279000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_sec_bitstream
+ &venus_domain_sec_pixel &venus_domain_sec_non_pixel>;
+ qcom,iommu-group-buffer-types = <0xfff 0x91 0x42 0x120>;
+ qcom,buffer-type-tz-usage-table = <0x91 0x1>,
+ <0x42 0x2>,
+ <0x120 0x3>;
};
&krait_pdn {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 2c4d5d9..7c6a9d1 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -31,6 +31,15 @@
spi7 = &spi_7;
};
+ memory {
+
+ secure_mem: region@0 {
+ linux,contiguous-region;
+ reg = <0 0x7800000>;
+ label = "secure_mem";
+ };
+ };
+
intc: interrupt-controller@F9000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 3d710cc..e6f4396 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -220,6 +220,7 @@
CONFIG_CRYPTO_TWOFISH=y
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
+CONFIG_QPNP_POWER_ON=y
CONFIG_LIBCRC32C=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 5c03630..d36d5a2 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -234,6 +234,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_HAPTIC_ISA1200=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index faa0471..df0b5f0 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -238,6 +238,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_TSPP=m
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 7c2c463..833b213 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -302,81 +302,56 @@
#define bam_ch_is_in_reset(x) \
(bam_ch[(x)].status & BAM_CH_IN_RESET)
-#define LOG_MESSAGE_MAX_SIZE 80
struct kfifo bam_dmux_state_log;
-static uint32_t bam_dmux_state_logging_disabled;
static int bam_dmux_uplink_vote;
static int bam_dmux_power_state;
-static void bam_dmux_log(const char *fmt, ...)
- __printf(1, 2);
-
-
-#define DMUX_LOG_KERR(fmt...) \
-do { \
- bam_dmux_log(fmt); \
- pr_err(fmt); \
-} while (0)
-
static void *bam_ipc_log_txt;
#define BAM_IPC_LOG_PAGES 5
/**
* Log a state change along with a small message.
- *
* Complete size of messsage is limited to @todo.
+ * Logging is done using IPC Logging infrastructure.
+ *
+ * States
+ * D: 1 = Power collapse disabled
+ * R: 1 = in global reset
+ * P: 1 = BAM is powered up
+ * A: 1 = BAM initialized and ready for data
+ * V: 1 = Uplink vote for power
+ * U: 1 = Uplink active
+ * W: 1 = Uplink Wait-for-ack
+ * A: 1 = Uplink ACK received
+ * #: >=1 On-demand uplink vote
+ * D: 1 = Disconnect ACK active
*/
-static void bam_dmux_log(const char *fmt, ...)
-{
- char buff[LOG_MESSAGE_MAX_SIZE];
- va_list arg_list;
- unsigned long long t_now;
- unsigned long nanosec_rem;
- int len = 0;
- if (bam_dmux_state_logging_disabled)
- return;
+#define BAM_DMUX_LOG(fmt, args...) \
+do { \
+ if (bam_ipc_log_txt) { \
+ ipc_log_string(bam_ipc_log_txt, \
+ "<DMUX> %c%c%c%c %c%c%c%c%d%c " fmt, \
+ a2_pc_disabled ? 'D' : 'd', \
+ in_global_reset ? 'R' : 'r', \
+ bam_dmux_power_state ? 'P' : 'p', \
+ bam_connection_is_active ? 'A' : 'a', \
+ bam_dmux_uplink_vote ? 'V' : 'v', \
+ bam_is_connected ? 'U' : 'u', \
+ wait_for_ack ? 'W' : 'w', \
+ ul_wakeup_ack_completion.done ? 'A' : 'a', \
+ atomic_read(&ul_ondemand_vote), \
+ disconnect_ack ? 'D' : 'd', \
+ args); \
+ } \
+} while (0)
- t_now = sched_clock();
- nanosec_rem = do_div(t_now, 1000000000U);
-
- /*
- * States
- * D: 1 = Power collapse disabled
- * R: 1 = in global reset
- * P: 1 = BAM is powered up
- * A: 1 = BAM initialized and ready for data
- *
- * V: 1 = Uplink vote for power
- * U: 1 = Uplink active
- * W: 1 = Uplink Wait-for-ack
- * A: 1 = Uplink ACK received
- * #: >=1 On-demand uplink vote
- * D: 1 = Disconnect ACK active
- */
- len += scnprintf(buff, sizeof(buff),
- "<DMUX> %u.%09lu %c%c%c%c %c%c%c%c%d%c ",
- (unsigned)t_now, nanosec_rem,
- a2_pc_disabled ? 'D' : 'd',
- in_global_reset ? 'R' : 'r',
- bam_dmux_power_state ? 'P' : 'p',
- bam_connection_is_active ? 'A' : 'a',
- bam_dmux_uplink_vote ? 'V' : 'v',
- bam_is_connected ? 'U' : 'u',
- wait_for_ack ? 'W' : 'w',
- ul_wakeup_ack_completion.done ? 'A' : 'a',
- atomic_read(&ul_ondemand_vote),
- disconnect_ack ? 'D' : 'd'
- );
-
- va_start(arg_list, fmt);
- len += vscnprintf(buff + len, sizeof(buff) - len, fmt, arg_list);
- va_end(arg_list);
- memset(buff + len, 0x0, sizeof(buff) - len);
- if (bam_ipc_log_txt)
- ipc_log_string(bam_ipc_log_txt, buff);
-}
+#define DMUX_LOG_KERR(fmt, args...) \
+do { \
+ BAM_DMUX_LOG(fmt, args); \
+ pr_err(fmt, args); \
+} while (0)
static inline void set_tx_timestamp(struct tx_pkt_info *pkt)
{
@@ -396,12 +371,12 @@
spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
list_for_each_entry(info, &bam_tx_pool, list_node) {
if (!reported) {
- bam_dmux_log("%s: tx pool not empty\n", func);
+ BAM_DMUX_LOG("%s: tx pool not empty\n", func);
if (!in_global_reset)
pr_err("%s: tx pool not empty\n", func);
reported = 1;
}
- bam_dmux_log("%s: node=%p ts=%u.%09lu\n", __func__,
+ BAM_DMUX_LOG("%s: node=%p ts=%u.%09lu\n", __func__,
&info->list_node, info->ts_sec, info->ts_nsec);
if (!in_global_reset)
pr_err("%s: node=%p ts=%u.%09lu\n", __func__,
@@ -529,7 +504,7 @@
mutex_lock(&bam_pdev_mutexlock);
if (in_global_reset) {
- bam_dmux_log("%s: open cid %d aborted due to ssr\n",
+ BAM_DMUX_LOG("%s: open cid %d aborted due to ssr\n",
__func__, rx_hdr->ch_id);
mutex_unlock(&bam_pdev_mutexlock);
queue_rx();
@@ -593,18 +568,18 @@
bam_mux_process_data(rx_skb);
break;
case BAM_MUX_HDR_CMD_OPEN:
- bam_dmux_log("%s: opening cid %d PC enabled\n", __func__,
+ BAM_DMUX_LOG("%s: opening cid %d PC enabled\n", __func__,
rx_hdr->ch_id);
handle_bam_mux_cmd_open(rx_hdr);
if (!(rx_hdr->reserved & ENABLE_DISCONNECT_ACK)) {
- bam_dmux_log("%s: deactivating disconnect ack\n",
+ BAM_DMUX_LOG("%s: deactivating disconnect ack\n",
__func__);
disconnect_ack = 0;
}
dev_kfree_skb_any(rx_skb);
break;
case BAM_MUX_HDR_CMD_OPEN_NO_A2_PC:
- bam_dmux_log("%s: opening cid %d PC disabled\n", __func__,
+ BAM_DMUX_LOG("%s: opening cid %d PC disabled\n", __func__,
rx_hdr->ch_id);
if (!a2_pc_disabled) {
@@ -617,11 +592,11 @@
break;
case BAM_MUX_HDR_CMD_CLOSE:
/* probably should drop pending write */
- bam_dmux_log("%s: closing cid %d\n", __func__,
+ BAM_DMUX_LOG("%s: closing cid %d\n", __func__,
rx_hdr->ch_id);
mutex_lock(&bam_pdev_mutexlock);
if (in_global_reset) {
- bam_dmux_log("%s: close cid %d aborted due to ssr\n",
+ BAM_DMUX_LOG("%s: close cid %d aborted due to ssr\n",
__func__, rx_hdr->ch_id);
mutex_unlock(&bam_pdev_mutexlock);
break;
@@ -1412,7 +1387,7 @@
for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
if (bam_ch_is_open(i)) {
bam_ch[i].notify(bam_ch[i].priv, event, data);
- bam_dmux_log("%s: cid=%d, event=%d, data=%lu\n",
+ BAM_DMUX_LOG("%s: cid=%d, event=%d, data=%lu\n",
__func__, i, event, data);
}
}
@@ -1455,11 +1430,11 @@
static void power_vote(int vote)
{
- bam_dmux_log("%s: curr=%d, vote=%d\n", __func__,
+ BAM_DMUX_LOG("%s: curr=%d, vote=%d\n", __func__,
bam_dmux_uplink_vote, vote);
if (bam_dmux_uplink_vote == vote)
- bam_dmux_log("%s: warning - duplicate power vote\n", __func__);
+ BAM_DMUX_LOG("%s: warning - duplicate power vote\n", __func__);
bam_dmux_uplink_vote = vote;
if (vote)
@@ -1473,7 +1448,7 @@
*/
static inline void ul_powerdown(void)
{
- bam_dmux_log("%s: powerdown\n", __func__);
+ BAM_DMUX_LOG("%s: powerdown\n", __func__);
verify_tx_queue_is_empty(__func__);
if (a2_pc_disabled) {
@@ -1585,7 +1560,7 @@
}
if (ul_packet_written || atomic_read(&ul_ondemand_vote)) {
- bam_dmux_log("%s: pkt written %d\n",
+ BAM_DMUX_LOG("%s: pkt written %d\n",
__func__, ul_packet_written);
ul_packet_written = 0;
schedule_delayed_work(&ul_timeout_work,
@@ -1614,7 +1589,7 @@
mutex_lock(&wakeup_lock);
if (bam_is_connected) { /* bam got connected before lock grabbed */
- bam_dmux_log("%s Already awake\n", __func__);
+ BAM_DMUX_LOG("%s Already awake\n", __func__);
mutex_unlock(&wakeup_lock);
return;
}
@@ -1677,35 +1652,35 @@
* instead of waiting
*/
if (wait_for_ack) {
- bam_dmux_log("%s waiting for previous ack\n", __func__);
+ BAM_DMUX_LOG("%s waiting for previous ack\n", __func__);
ret = wait_for_completion_timeout(
&ul_wakeup_ack_completion, HZ);
wait_for_ack = 0;
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout previous ack\n", __func__);
+ BAM_DMUX_LOG("%s timeout previous ack\n", __func__);
return;
}
}
INIT_COMPLETION(ul_wakeup_ack_completion);
power_vote(1);
- bam_dmux_log("%s waiting for wakeup ack\n", __func__);
+ BAM_DMUX_LOG("%s waiting for wakeup ack\n", __func__);
ret = wait_for_completion_timeout(&ul_wakeup_ack_completion, HZ);
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout wakeup ack\n", __func__);
+ BAM_DMUX_LOG("%s timeout wakeup ack\n", __func__);
return;
}
- bam_dmux_log("%s waiting completion\n", __func__);
+ BAM_DMUX_LOG("%s waiting completion\n", __func__);
ret = wait_for_completion_timeout(&bam_connection_completion, HZ);
if (unlikely(ret == 0) && ssrestart_check()) {
mutex_unlock(&wakeup_lock);
- bam_dmux_log("%s timeout power on\n", __func__);
+ BAM_DMUX_LOG("%s timeout power on\n", __func__);
return;
}
bam_is_connected = 1;
- bam_dmux_log("%s complete\n", __func__);
+ BAM_DMUX_LOG("%s complete\n", __func__);
schedule_delayed_work(&ul_timeout_work,
msecs_to_jiffies(UL_TIMEOUT_DELAY));
mutex_unlock(&wakeup_lock);
@@ -1771,7 +1746,7 @@
/* handle disconnect during active UL */
write_lock_irqsave(&ul_wakeup_lock, flags);
if (bam_is_connected) {
- bam_dmux_log("%s: UL active - forcing powerdown\n", __func__);
+ BAM_DMUX_LOG("%s: UL active - forcing powerdown\n", __func__);
ul_powerdown();
}
write_unlock_irqrestore(&ul_wakeup_lock, flags);
@@ -1817,10 +1792,10 @@
{
int rc;
- bam_dmux_log("%s\n", __func__);
+ BAM_DMUX_LOG("%s\n", __func__);
mutex_lock(&dfab_status_lock);
if (dfab_is_on) {
- bam_dmux_log("%s: dfab is already on\n", __func__);
+ BAM_DMUX_LOG("%s: dfab is already on\n", __func__);
mutex_unlock(&dfab_status_lock);
return;
}
@@ -1842,7 +1817,7 @@
static void unvote_dfab(void)
{
- bam_dmux_log("%s\n", __func__);
+ BAM_DMUX_LOG("%s\n", __func__);
mutex_lock(&dfab_status_lock);
if (!dfab_is_on) {
DMUX_LOG_KERR("%s: dfab is already off\n", __func__);
@@ -1864,7 +1839,7 @@
unsigned long flags;
spin_lock_irqsave(&wakelock_reference_lock, flags);
- bam_dmux_log("%s: ref count = %d\n", __func__,
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
wakelock_reference_count);
if (wakelock_reference_count == 0)
wake_lock(&bam_wakelock);
@@ -1883,7 +1858,7 @@
spin_unlock_irqrestore(&wakelock_reference_lock, flags);
return;
}
- bam_dmux_log("%s: ref count = %d\n", __func__,
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
wakelock_reference_count);
--wakelock_reference_count;
if (wakelock_reference_count == 0)
@@ -1917,7 +1892,7 @@
if (code == SUBSYS_BEFORE_SHUTDOWN) {
in_global_reset = 1;
in_ssr = 1;
- bam_dmux_log("%s: begin\n", __func__);
+ BAM_DMUX_LOG("%s: begin\n", __func__);
flush_workqueue(bam_mux_rx_workqueue);
}
if (code != SUBSYS_AFTER_SHUTDOWN)
@@ -1978,7 +1953,7 @@
}
spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
- bam_dmux_log("%s: complete\n", __func__);
+ BAM_DMUX_LOG("%s: complete\n", __func__);
return NOTIFY_DONE;
}
@@ -2225,7 +2200,7 @@
{
static unsigned int clear_bit; /* 0 = set the bit, else clear bit */
- bam_dmux_log("%s: apps ack %d->%d\n", __func__,
+ BAM_DMUX_LOG("%s: apps ack %d->%d\n", __func__,
clear_bit & 0x1, ~clear_bit & 0x1);
smsm_change_state(SMSM_APPS_STATE,
clear_bit & SMSM_A2_POWER_CONTROL_ACK,
@@ -2241,10 +2216,10 @@
mutex_lock(&smsm_cb_lock);
bam_dmux_power_state = new_state & SMSM_A2_POWER_CONTROL ? 1 : 0;
DBG_INC_A2_POWER_CONTROL_IN_CNT();
- bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
new_state);
if (last_processed_state == (new_state & SMSM_A2_POWER_CONTROL)) {
- bam_dmux_log("%s: already processed this state\n", __func__);
+ BAM_DMUX_LOG("%s: already processed this state\n", __func__);
mutex_unlock(&smsm_cb_lock);
return;
}
@@ -2252,23 +2227,23 @@
last_processed_state = new_state & SMSM_A2_POWER_CONTROL;
if (bam_mux_initialized && new_state & SMSM_A2_POWER_CONTROL) {
- bam_dmux_log("%s: reconnect\n", __func__);
+ BAM_DMUX_LOG("%s: reconnect\n", __func__);
grab_wakelock();
reconnect_to_bam();
} else if (bam_mux_initialized &&
!(new_state & SMSM_A2_POWER_CONTROL)) {
- bam_dmux_log("%s: disconnect\n", __func__);
+ BAM_DMUX_LOG("%s: disconnect\n", __func__);
disconnect_to_bam();
release_wakelock();
} else if (new_state & SMSM_A2_POWER_CONTROL) {
- bam_dmux_log("%s: init\n", __func__);
+ BAM_DMUX_LOG("%s: init\n", __func__);
grab_wakelock();
if (cpu_is_msm9615())
msm9615_bam_init();
else
bam_init();
} else {
- bam_dmux_log("%s: bad state change\n", __func__);
+ BAM_DMUX_LOG("%s: bad state change\n", __func__);
pr_err("%s: unsupported state change\n", __func__);
}
mutex_unlock(&smsm_cb_lock);
@@ -2279,7 +2254,7 @@
uint32_t new_state)
{
DBG_INC_ACK_IN_CNT();
- bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
new_state);
complete_all(&ul_wakeup_ack_completion);
}
@@ -2322,12 +2297,12 @@
xo_clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(xo_clk)) {
- bam_dmux_log("%s: did not get xo clock\n", __func__);
+ BAM_DMUX_LOG("%s: did not get xo clock\n", __func__);
xo_clk = NULL;
}
dfab_clk = clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(dfab_clk)) {
- bam_dmux_log("%s: did not get dfab clock\n", __func__);
+ BAM_DMUX_LOG("%s: did not get dfab clock\n", __func__);
dfab_clk = NULL;
} else {
rc = clk_set_rate(dfab_clk, 64000000);
@@ -2434,7 +2409,6 @@
bam_ipc_log_txt = ipc_log_context_create(BAM_IPC_LOG_PAGES, "bam_dmux");
if (!bam_ipc_log_txt) {
pr_err("%s : unable to create IPC Logging Context", __func__);
- bam_dmux_state_logging_disabled = 1;
}
rx_timer_interval = DEFAULT_POLLING_MIN_SLEEP;
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 0dee8f5..0f88287 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -1729,13 +1729,6 @@
},
};
-static struct gpiomux_setting fsm8064_ep_sync_drsync_cfg = {
- .func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_UP,
- .dir = GPIOMUX_OUT_HIGH,
-};
-
static struct gpiomux_setting fsm8064_ep_sync_input_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_4MA,
@@ -1746,7 +1739,7 @@
{
.gpio = 6, /* GPSPPSIN_DRSYNC */
.settings = {
- [GPIOMUX_SUSPENDED] = &fsm8064_ep_sync_drsync_cfg,
+ [GPIOMUX_SUSPENDED] = &fsm8064_ep_sync_input_cfg,
},
},
{
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index 0b3366c..a1ed251 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -137,7 +137,7 @@
PM8921_GPIO_OUTPUT_VIN(14, 1, PM_GPIO_VIN_VPH),
/* PPS_SRC_SEL_N, chooses between WGR7640 PPS source (high) or
* CW GPS module PPS source (low) */
- PM8921_GPIO_OUTPUT_VIN(19, 1, PM_GPIO_VIN_VPH), /* PPS_SRC_SEL_N */
+ PM8921_GPIO_OUTPUT_VIN(19, 0, PM_GPIO_VIN_VPH), /* PPS_SRC_SEL_N */
PM8921_GPIO_OUTPUT_VIN(13, 1, PM_GPIO_VIN_VPH), /* PCIE_CLK_PWR_EN */
PM8921_GPIO_OUTPUT_VIN(37, 1, PM_GPIO_VIN_VPH), /* PCIE_RST_N */
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 9ed71da..f3d648e 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -4005,6 +4005,7 @@
.init_early = apq8064_allocate_memory_regions,
.init_very_early = apq8064_early_reserve,
.restart = msm_restart,
+ .smp = &msm8960_smp_ops,
MACHINE_END
MACHINE_START(APQ8064_MTP, "QCT APQ8064 MTP")
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 7038ab9..9c9ccaa 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -93,9 +93,9 @@
"qup_scl" },
{ GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
"qup_sda" },
- { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_scl" },
- { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_sda" },
};
@@ -104,9 +104,9 @@
"qup_scl" },
{ GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
"qup_sda" },
- { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_scl" },
- { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
"qup_sda" },
};
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index bfa9ec0..4cef377 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -809,6 +809,7 @@
static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, &cxo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_ehci_host_clk, &cxo_clk_src.c);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -4893,6 +4894,7 @@
CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
CLK_LOOKUP("xo", cxo_dwc3_clk.c, "msm_dwc3"),
+ CLK_LOOKUP("xo", cxo_ehci_host_clk.c, "msm_ehci_host"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
@@ -5033,9 +5035,11 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6e.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "20.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk2_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6e.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "20.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, ""),
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
index 1073266..f2a4427 100644
--- a/arch/arm/mach-msm/include/mach/ipa.h
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -463,26 +463,28 @@
enum a2_mux_event_type event,
unsigned long data);
-#ifdef CONFIG_IPA
-
-/*
- * a2 service
+/**
+ * enum teth_tethering_mode - Tethering mode (Rmnet / MBIM)
*/
-int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
- void *user_data,
- a2_mux_notify_cb notify_cb);
+enum teth_tethering_mode {
+ TETH_TETHERING_MODE_RMNET,
+ TETH_TETHERING_MODE_MBIM,
+ TETH_TETHERING_MODE_MAX,
+};
-int a2_mux_close_channel(enum a2_mux_logical_channel_id lcid);
+/**
+ * struct teth_bridge_connect_params - Parameters used in teth_bridge_connect()
+ * @ipa_usb_pipe_hdl: IPA to USB pipe handle, returned from ipa_connect()
+ * @usb_ipa_pipe_hdl: USB to IPA pipe handle, returned from ipa_connect()
+ * @tethering_mode: Rmnet or MBIM
+ */
+struct teth_bridge_connect_params {
+ u32 ipa_usb_pipe_hdl;
+ u32 usb_ipa_pipe_hdl;
+ enum teth_tethering_mode tethering_mode;
+};
-int a2_mux_write(enum a2_mux_logical_channel_id lcid, struct sk_buff *skb);
-
-int a2_mux_is_ch_low(enum a2_mux_logical_channel_id lcid);
-
-int a2_mux_is_ch_full(enum a2_mux_logical_channel_id lcid);
-
-int a2_mux_get_tethered_client_handles(enum a2_mux_logical_channel_id lcid,
- unsigned int *clnt_cons_handle,
- unsigned int *clnt_prod_handle);
+#ifdef CONFIG_IPA
/*
* Connect / Disconnect
@@ -652,6 +654,34 @@
int ipa_rm_inactivity_timer_release_resource(
enum ipa_rm_resource_name resource_name);
+/*
+ * a2 service
+ */
+int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
+ void *user_data,
+ a2_mux_notify_cb notify_cb);
+
+int a2_mux_close_channel(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_write(enum a2_mux_logical_channel_id lcid, struct sk_buff *skb);
+
+int a2_mux_is_ch_low(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_is_ch_full(enum a2_mux_logical_channel_id lcid);
+
+int a2_mux_get_tethered_client_handles(enum a2_mux_logical_channel_id lcid,
+ unsigned int *clnt_cons_handle,
+ unsigned int *clnt_prod_handle);
+
+/*
+ * Tethering bridge (Rmnet / MBIM)
+ */
+int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr);
+
+int teth_bridge_disconnect(void);
+
+int teth_bridge_connect(struct teth_bridge_connect_params *connect_params);
+
#else /* CONFIG_IPA */
static inline int a2_mux_open_channel(enum a2_mux_logical_channel_id lcid,
@@ -688,7 +718,6 @@
return -EPERM;
}
-
/*
* Connect / Disconnect
*/
@@ -1054,6 +1083,26 @@
return -EPERM;
}
+/*
+ * Tethering bridge (Rmnetm / MBIM)
+ */
+static inline int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr,
+ void **private_data_ptr)
+{
+ return -EPERM;
+}
+
+static inline int teth_bridge_disconnect(void)
+{
+ return -EPERM;
+}
+
+static inline int teth_bridge_connect(struct teth_bridge_connect_params
+ *connect_params)
+{
+ return -EPERM;
+}
+
#endif /* CONFIG_IPA*/
#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index 2cec5c5..a08e7de 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -20,6 +20,9 @@
#include <linux/fcntl.h>
#include <linux/gfp.h>
#include <linux/msm_ipc.h>
+#include <linux/sched.h>
+#include <linux/thread_info.h>
+#include <linux/qmi_encdec.h>
#include <asm/string.h>
#include <asm/atomic.h>
@@ -27,16 +30,93 @@
#include <net/sock.h>
#include <mach/msm_ipc_router.h>
+#include <mach/msm_ipc_logging.h>
#include "ipc_router.h"
#include "msm_ipc_router_security.h"
#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
+#define REQ_RESP_IPC_LOG_PAGES 5
+#define IND_IPC_LOG_PAGES 5
+#define IPC_SEND 1
+#define IPC_RECV 2
+#define IPC_REQ_RESP_LOG(level, buf...) \
+do { \
+ if (ipc_req_resp_log_txt) { \
+ ipc_log_string(ipc_req_resp_log_txt, buf); \
+ } \
+} while (0) \
+
+#define IPC_IND_LOG(level, buf...) \
+do { \
+ if (ipc_ind_log_txt) { \
+ ipc_log_string(ipc_ind_log_txt, buf); \
+ } \
+} while (0) \
static int sockets_enabled;
static struct proto msm_ipc_proto;
static const struct proto_ops msm_ipc_proto_ops;
+static void *ipc_req_resp_log_txt;
+static void *ipc_ind_log_txt;
+
+/**
+ * msm_ipc_router_ipc_log() - Pass log data to IPC logging framework
+ * @tran: Identifies the data to be a receive or send.
+ * @ipc_buf: Buffer to extract the log data.
+ * @port_ptr: IPC Router port corresponding to the current log data.
+ *
+ * This function builds the data the would be passed on to the IPC logging
+ * framework. The data that would be passed corresponds to the information
+ * that is exchanged between the IPC Router and user space modules during
+ * request/response/indication transactions.
+ */
+
+static void msm_ipc_router_ipc_log(uint8_t tran,
+ struct sk_buff *ipc_buf, struct msm_ipc_port *port_ptr)
+{
+ struct qmi_header *hdr = (struct qmi_header *)ipc_buf->data;
+
+ /*
+ * IPC Logging format is as below:-
+ * <Name>(Name of the User Space Process):
+ * <PID> (PID of the user space process) :
+ * <TID> (TID of the user space thread) :
+ * <User Space Module>(CLNT or SERV) :
+ * <Opertaion Type> (Transmit) :
+ * <Control Flag> (Req/Resp/Ind) :
+ * <Transaction ID> :
+ * <Message ID> :
+ * <Message Length> :
+ */
+ if (ipc_req_resp_log_txt &&
+ (((uint8_t) hdr->cntl_flag == QMI_REQUEST_CONTROL_FLAG) ||
+ ((uint8_t) hdr->cntl_flag == QMI_RESPONSE_CONTROL_FLAG)) &&
+ (port_ptr->type == CLIENT_PORT ||
+ port_ptr->type == SERVER_PORT)) {
+ IPC_REQ_RESP_LOG(KERN_DEBUG,
+ "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
+ current->comm, current->tgid, current->pid,
+ (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
+ (tran == IPC_RECV ? "RX" :
+ (tran == IPC_SEND ? "TX" : "ERR")),
+ (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
+ hdr->msg_len);
+ } else if (ipc_ind_log_txt &&
+ ((uint8_t)hdr->cntl_flag == QMI_INDICATION_CONTROL_FLAG) &&
+ (port_ptr->type == CLIENT_PORT ||
+ port_ptr->type == SERVER_PORT)) {
+ IPC_IND_LOG(KERN_DEBUG,
+ "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
+ current->comm, current->tgid, current->pid,
+ (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
+ (tran == IPC_RECV ? "RX" :
+ (tran == IPC_SEND ? "TX" : "ERR")),
+ (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
+ hdr->msg_len);
+ }
+}
static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
struct iovec const *msg_sect,
@@ -263,6 +343,7 @@
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
struct sk_buff_head *msg;
+ struct sk_buff *ipc_buf;
int ret;
if (!dest)
@@ -284,7 +365,8 @@
if (port_ptr->type == CLIENT_PORT)
wait_for_irsc_completion();
-
+ ipc_buf = skb_peek(msg);
+ msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
ret = total_len;
@@ -300,6 +382,7 @@
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct sk_buff_head *msg;
+ struct sk_buff *ipc_buf;
long timeout;
int ret;
@@ -344,6 +427,8 @@
}
ret = msm_ipc_router_extract_msg(m, msg);
+ ipc_buf = skb_peek(msg);
+ msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
msm_ipc_router_release_msg(msg);
msg = NULL;
release_sock(sk);
@@ -518,6 +603,29 @@
.obj_size = sizeof(struct msm_ipc_sock),
};
+/**
+ * msm_ipc_router_ipc_log_init() - Init function for IPC Logging
+ *
+ * Initialize the buffers to be used to provide the log information
+ * pertaining to the request, response and indication data flow that
+ * happens between user and kernel spaces.
+ */
+void msm_ipc_router_ipc_log_init(void)
+{
+ ipc_req_resp_log_txt =
+ ipc_log_context_create(REQ_RESP_IPC_LOG_PAGES, "req_resp");
+ if (!ipc_req_resp_log_txt) {
+ pr_err("%s: Unable to create IPC logging for Req/Resp",
+ __func__);
+ }
+ ipc_ind_log_txt =
+ ipc_log_context_create(IND_IPC_LOG_PAGES, "indication");
+ if (!ipc_ind_log_txt) {
+ pr_err("%s: Unable to create IPC logging for Indications",
+ __func__);
+ }
+}
+
int msm_ipc_router_init_sockets(void)
{
int ret;
@@ -536,6 +644,7 @@
}
sockets_enabled = 1;
+ msm_ipc_router_ipc_log_init();
out_init_sockets:
return ret;
}
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 1820b23..5969a3c 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -47,7 +47,7 @@
struct smd_tty_info {
smd_channel_t *ch;
- struct tty_struct *tty;
+ struct tty_port port;
struct wake_lock wake_lock;
int open_count;
struct tasklet_struct tty_tsklt;
@@ -125,7 +125,7 @@
unsigned char *ptr;
int avail;
struct smd_tty_info *info = (struct smd_tty_info *)param;
- struct tty_struct *tty = info->tty;
+ struct tty_struct *tty = tty_port_tty_get(&info->port);
unsigned long flags;
if (!tty)
@@ -156,6 +156,7 @@
if (avail <= 0) {
mod_timer(&info->buf_req_timer,
jiffies + msecs_to_jiffies(30));
+ tty_kref_put(tty);
return;
}
@@ -173,11 +174,13 @@
/* XXX only when writable and necessary */
tty_wakeup(tty);
+ tty_kref_put(tty);
}
static void smd_tty_notify(void *priv, unsigned event)
{
struct smd_tty_info *info = priv;
+ struct tty_struct *tty;
unsigned long flags;
switch (event) {
@@ -195,8 +198,10 @@
*/
if (smd_write_avail(info->ch)) {
smd_disable_read_intr(info->ch);
- if (info->tty)
- wake_up_interruptible(&info->tty->write_wait);
+ tty = tty_port_tty_get(&info->port);
+ if (tty)
+ wake_up_interruptible(&tty->write_wait);
+ tty_kref_put(tty);
}
spin_lock_irqsave(&info->ra_lock, flags);
if (smd_read_avail(info->ch)) {
@@ -225,9 +230,11 @@
/* schedule task to send TTY_BREAK */
tasklet_hi_schedule(&info->tty_tsklt);
- if (info->tty->index == LOOPBACK_IDX)
+ tty = tty_port_tty_get(&info->port);
+ if (tty->index == LOOPBACK_IDX)
schedule_delayed_work(&loopback_work,
msecs_to_jiffies(1000));
+ tty_kref_put(tty);
break;
}
}
@@ -241,7 +248,8 @@
return (modem_state & ready_state) == ready_state;
}
-static int smd_tty_open(struct tty_struct *tty, struct file *f)
+static int smd_tty_port_activate(struct tty_port *tport,
+ struct tty_struct *tty)
{
int res = 0;
unsigned int n = tty->index;
@@ -306,8 +314,6 @@
}
}
-
- info->tty = tty;
tasklet_init(&info->tty_tsklt, smd_tty_read,
(unsigned long)info);
wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND,
@@ -354,24 +360,27 @@
return res;
}
-static void smd_tty_close(struct tty_struct *tty, struct file *f)
+static void smd_tty_port_shutdown(struct tty_port *tport)
{
- struct smd_tty_info *info = tty->driver_data;
+ struct smd_tty_info *info;
+ struct tty_struct *tty = tty_port_tty_get(tport);
unsigned long flags;
- if (info == 0)
+ info = tty->driver_data;
+ if (info == 0) {
+ tty_kref_put(tty);
return;
+ }
mutex_lock(&smd_tty_lock);
if (--info->open_count == 0) {
spin_lock_irqsave(&info->reset_lock, flags);
info->is_open = 0;
spin_unlock_irqrestore(&info->reset_lock, flags);
- if (info->tty) {
+ if (tty) {
tasklet_kill(&info->tty_tsklt);
wake_lock_destroy(&info->wake_lock);
wake_lock_destroy(&info->ra_wake_lock);
- info->tty = 0;
}
tty->driver_data = 0;
del_timer(&info->buf_req_timer);
@@ -382,6 +391,21 @@
}
}
mutex_unlock(&smd_tty_lock);
+ tty_kref_put(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = smd_tty + tty->index;
+
+ return tty_port_open(&info->port, tty, f);
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+ struct smd_tty_info *info = tty->driver_data;
+
+ tty_port_close(&info->port, tty, f);
}
static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
@@ -482,6 +506,11 @@
0, SMSM_SMD_LOOPBACK);
}
+static const struct tty_port_operations smd_tty_port_ops = {
+ .shutdown = smd_tty_port_shutdown,
+ .activate = smd_tty_port_activate,
+};
+
static struct tty_operations smd_tty_ops = {
.open = smd_tty_open,
.close = smd_tty_close,
@@ -523,6 +552,7 @@
int ret;
int n;
int idx;
+ struct tty_port *port;
smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
if (smd_tty_driver == 0)
@@ -578,6 +608,10 @@
continue;
}
+ port = &smd_tty[idx].port;
+ tty_port_init(port);
+ port->ops = &smd_tty_port_ops;
+ /* TODO: For kernel >= 3.7 use tty_port_register_device */
tty_register_device(smd_tty_driver, idx, 0);
init_completion(&smd_tty[idx].ch_allocated);
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index e879026..3a8bbc5 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -58,6 +58,7 @@
phys_addr_t base;
unsigned long size;
struct cma *cma;
+ const char *name;
} cma_areas[MAX_CMA_AREAS];
static unsigned cma_area_count;
@@ -77,6 +78,20 @@
return NULL;
}
+static struct cma *cma_get_area_by_name(const char *name)
+{
+ int i;
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < cma_area_count; i++)
+ if (cma_areas[i].name && strcmp(cma_areas[i].name, name) == 0)
+ return cma_areas[i].cma;
+ return NULL;
+}
+
+
+
#ifdef CONFIG_CMA_SIZE_MBYTES
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
#else
@@ -196,6 +211,7 @@
phys_addr_t base, size;
unsigned long len;
__be32 *prop;
+ char *name;
if (strncmp(uname, "region@", 7) != 0 || depth != 2 ||
!of_get_flat_dt_prop(node, "linux,contiguous-region", NULL))
@@ -208,9 +224,11 @@
base = be32_to_cpu(prop[0]);
size = be32_to_cpu(prop[1]);
+ name = of_get_flat_dt_prop(node, "label", NULL);
+
pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
(unsigned long)base, (unsigned long)size / SZ_1M);
- dma_contiguous_reserve_area(size, &base, 0);
+ dma_contiguous_reserve_area(size, &base, 0, name);
return 0;
}
@@ -251,7 +269,8 @@
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
(unsigned long)sel_size / SZ_1M);
- if (dma_contiguous_reserve_area(sel_size, &base, limit) == 0)
+ if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL)
+ == 0)
dma_contiguous_def_base = base;
}
#ifdef CONFIG_OF
@@ -274,7 +293,7 @@
* devices.
*/
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base,
- phys_addr_t limit)
+ phys_addr_t limit, const char *name)
{
phys_addr_t base = *res_base;
phys_addr_t alignment;
@@ -326,6 +345,7 @@
*/
cma_areas[cma_area_count].base = base;
cma_areas[cma_area_count].size = size;
+ cma_areas[cma_area_count].name = name;
cma_area_count++;
*res_base = base;
@@ -366,6 +386,7 @@
{
struct device_node *node;
struct cma *cma;
+ const char *name;
u32 value;
node = of_parse_phandle(dev->of_node, "linux,contiguous-region", 0);
@@ -373,7 +394,11 @@
return;
if (of_property_read_u32(node, "reg", &value) && !value)
return;
- cma = cma_get_area(value);
+
+ if (of_property_read_string(node, "label", &name))
+ return;
+
+ cma = cma_get_area_by_name(name);
if (!cma)
return;
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index b94ea2f..2369c4d 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 2012-2013, The Linux Foundation.
+ * All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -172,11 +173,14 @@
uint8_t src_byte;
int pkt_bnd = 0;
+ int msg_start;
if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
(hdlc->src_size - hdlc->src_idx > 0) &&
(hdlc->dest_size - hdlc->dest_idx > 0)) {
+ msg_start = (hdlc->src_idx == 0) ? 1 : 0;
+
src_ptr = hdlc->src_ptr;
src_ptr = &src_ptr[hdlc->src_idx];
src_length = hdlc->src_size - hdlc->src_idx;
@@ -203,8 +207,16 @@
}
} else if (src_byte == CONTROL_CHAR) {
dest_ptr[len++] = src_byte;
- pkt_bnd = 1;
+ /*
+ * If this is the first byte in the message,
+ * then it is part of the command. Otherwise,
+ * consider it as the last byte of the
+ * message.
+ */
+ if (msg_start && i == 0 && src_length > 1)
+ continue;
i++;
+ pkt_bnd = 1;
break;
} else {
dest_ptr[len++] = src_byte;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 7f4edd1..2aca8cf 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -993,10 +993,18 @@
ret = diag_hdlc_decode(&hdlc);
- if (hdlc.dest_idx < 3) {
- pr_err("diag: Integer underflow in hdlc processing\n");
+ /*
+ * If the message is 3 bytes or less in length then the message is
+ * too short. A message will need 4 bytes minimum, since there are
+ * 2 bytes for the CRC and 1 byte for the ending 0x7e for the hdlc
+ * encoding
+ */
+ if (hdlc.dest_idx < 4) {
+ pr_err_ratelimited("diag: In %s, message is too short, len: %d, dest len: %d\n",
+ __func__, len, hdlc.dest_idx);
return;
}
+
if (ret) {
type = diag_process_apps_pkt(driver->hdlc_buf,
hdlc.dest_idx - 3);
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 1ca457f..832a9a1 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -55,7 +55,7 @@
},
{
.id = ION_CP_MM_HEAP_ID,
- .type = ION_HEAP_TYPE_CP,
+ .type = ION_HEAP_TYPE_SECURE_DMA,
.name = ION_MM_HEAP_NAME,
.permission_type = IPT_TYPE_MM_CARVEOUT,
},
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index b7d813c..b1a45bf 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2000,8 +2000,17 @@
/* Is the ring buffer is empty? */
GSL_RB_GET_READPTR(rb, &rb->rptr);
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
- /* Is the core idle? */
- status = is_adreno_rbbm_status_idle(device);
+ /*
+ * Are there interrupts pending? If so then pretend we
+ * are not idle - this avoids the possiblity that we go
+ * to a lower power state without handling interrupts
+ * first.
+ */
+
+ if (!adreno_dev->gpudev->irq_pending(adreno_dev)) {
+ /* Is the core idle? */
+ status = is_adreno_rbbm_status_idle(device);
+ }
}
} else {
status = true;
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index cc6eb16..b1cab9b 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -125,6 +125,7 @@
struct adreno_context *);
irqreturn_t (*irq_handler)(struct adreno_device *);
void (*irq_control)(struct adreno_device *, int);
+ unsigned int (*irq_pending)(struct adreno_device *);
void * (*snapshot)(struct adreno_device *, void *, int *, int);
void (*rb_init)(struct adreno_device *, struct adreno_ringbuffer *);
void (*start)(struct adreno_device *);
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 952d1f8..6db6e7b 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1706,34 +1706,6 @@
return;
}
- if (status & CP_INT_CNTL__RB_INT_MASK) {
- /* signal intr completion event */
- unsigned int context_id, timestamp;
- kgsl_sharedmem_readl(&device->memstore, &context_id,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- current_context));
-
- kgsl_sharedmem_readl(&device->memstore, ×tamp,
- KGSL_MEMSTORE_OFFSET(context_id,
- eoptimestamp));
-
- if (context_id < KGSL_MEMSTORE_MAX) {
- /* reset per context ts_cmp_enable */
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ts_cmp_enable), 0);
- /* Always reset global timestamp ts_cmp_enable */
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(
- KGSL_MEMSTORE_GLOBAL,
- ts_cmp_enable), 0);
- wmb();
- }
-
- KGSL_CMD_WARN(device, "<%d:0x%x> ringbuffer interrupt\n",
- context_id, timestamp);
- }
-
for (i = 0; i < ARRAY_SIZE(kgsl_cp_error_irqs); i++) {
if (status & kgsl_cp_error_irqs[i].mask) {
KGSL_CMD_CRIT(rb->device, "%s\n",
@@ -1840,6 +1812,19 @@
wmb();
}
+static unsigned int a2xx_irq_pending(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ unsigned int rbbm, cp, mh;
+
+ adreno_regread(device, REG_RBBM_INT_CNTL, &rbbm);
+ adreno_regread(device, REG_CP_INT_CNTL, &cp);
+ adreno_regread(device, MH_INTERRUPT_MASK, &mh);
+
+ return ((rbbm & RBBM_INT_MASK) || (cp & CP_INT_MASK) ||
+ (mh & kgsl_mmu_get_int_mask())) ? 1 : 0;
+}
+
static void a2xx_rb_init(struct adreno_device *adreno_dev,
struct adreno_ringbuffer *rb)
{
@@ -2035,6 +2020,7 @@
.ctxt_draw_workaround = a2xx_drawctxt_draw_workaround,
.irq_handler = a2xx_irq_handler,
.irq_control = a2xx_irq_control,
+ .irq_pending = a2xx_irq_pending,
.snapshot = a2xx_snapshot,
.rb_init = a2xx_rb_init,
.busy_cycles = a2xx_busy_cycles,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index a3739a2..73a7f52 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2591,33 +2591,7 @@
{
struct kgsl_device *device = &adreno_dev->dev;
- if (irq == A3XX_INT_CP_RB_INT) {
- unsigned int context_id, timestamp;
- kgsl_sharedmem_readl(&device->memstore, &context_id,
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- current_context));
-
- kgsl_sharedmem_readl(&device->memstore, ×tamp,
- KGSL_MEMSTORE_OFFSET(context_id,
- eoptimestamp));
-
- if (context_id < KGSL_MEMSTORE_MAX) {
- /* reset per context ts_cmp_enable */
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ts_cmp_enable), 0);
- /* Always reset global timestamp ts_cmp_enable */
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(
- KGSL_MEMSTORE_GLOBAL,
- ts_cmp_enable), 0);
- wmb();
- }
-
- KGSL_CMD_WARN(device, "<%d:0x%x> ringbuffer interrupt\n",
- context_id, timestamp);
- }
-
+ /* Wake up everybody waiting for the interrupt */
wake_up_interruptible_all(&device->wait_queue);
/* Schedule work to free mem and issue ibs */
@@ -2713,6 +2687,15 @@
adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
}
+static unsigned int a3xx_irq_pending(struct adreno_device *adreno_dev)
+{
+ unsigned int status;
+
+ adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status);
+
+ return (status & A3XX_INT_MASK) ? 1 : 0;
+}
+
static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = &adreno_dev->dev;
@@ -2958,6 +2941,7 @@
.rb_init = a3xx_rb_init,
.irq_control = a3xx_irq_control,
.irq_handler = a3xx_irq_handler,
+ .irq_pending = a3xx_irq_pending,
.busy_cycles = a3xx_busy_cycles,
.start = a3xx_start,
.snapshot = a3xx_snapshot,
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index c43ac51..1d25646 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -570,7 +570,7 @@
total_sizedwords += (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) ? 2 : 0;
/* Add CP_COND_EXEC commands to generate CP_INTERRUPT */
- total_sizedwords += context ? 7 : 0;
+ total_sizedwords += context ? 13 : 0;
if (adreno_is_a3xx(adreno_dev))
total_sizedwords += 7;
@@ -720,7 +720,25 @@
context_id, ref_wait_ts)) >> 2);
GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
/* # of conditional command DWORDs */
- GSL_RB_WRITE(ringcmds, rcmd_gpu, 2);
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 8);
+
+ /* Clear the ts_cmp_enable for the context */
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_MEM_WRITE, 2));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ KGSL_MEMSTORE_OFFSET(
+ context_id, ts_cmp_enable));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+
+ /* Clear the ts_cmp_enable for the global timestamp */
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ cp_type3_packet(CP_MEM_WRITE, 2));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, gpuaddr +
+ KGSL_MEMSTORE_OFFSET(
+ KGSL_MEMSTORE_GLOBAL, ts_cmp_enable));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0);
+
+ /* Trigger the interrupt */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_INTERRUPT, 1));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 115fcb7..7ed0b10 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1088,10 +1088,7 @@
result);
/* Fire off any pending suspend operations that are in flight */
-
- INIT_COMPLETION(dev_priv->device->suspend_gate);
- dev_priv->device->active_cnt--;
- complete(&dev_priv->device->suspend_gate);
+ kgsl_active_count_put(dev_priv->device);
return result;
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 62316f3..66390fc 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -133,6 +133,7 @@
void *priv;
struct list_head list;
void *owner;
+ unsigned int created;
};
@@ -449,4 +450,23 @@
kref_put(&context->refcount, kgsl_context_destroy);
}
+/**
+ * kgsl_active_count_put - Decrease the device active count
+ * @device: Pointer to a KGSL device
+ *
+ * Decrease the active count for the KGSL device and trigger the suspend_gate
+ * completion if it hits zero
+ */
+static inline void
+kgsl_active_count_put(struct kgsl_device *device)
+{
+ if (device->active_cnt == 1)
+ INIT_COMPLETION(device->suspend_gate);
+
+ device->active_cnt--;
+
+ if (device->active_cnt == 0)
+ complete(&device->suspend_gate);
+}
+
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index be9b5eb..6798eed 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -16,6 +16,8 @@
#include <linux/module.h>
#include <kgsl_device.h>
+#include "kgsl_trace.h"
+
static void _add_event_to_list(struct list_head *head, struct kgsl_event *event)
{
struct list_head *n;
@@ -71,6 +73,7 @@
*/
if (timestamp_cmp(cur_ts, ts) >= 0) {
+ trace_kgsl_fire_event(id, ts, 0);
cb(device, priv, id, ts);
return 0;
}
@@ -84,6 +87,9 @@
event->priv = priv;
event->func = cb;
event->owner = owner;
+ event->created = jiffies;
+
+ trace_kgsl_register_event(id, ts);
/* inc refcount to avoid race conditions in cleanup */
if (context)
@@ -106,6 +112,13 @@
} else
_add_event_to_list(&device->events, event);
+ /*
+ * Increase the active count on the device to avoid going into power
+ * saving modes while events are pending
+ */
+
+ device->active_cnt++;
+
queue_work(device->work_queue, &device->ts_expired_ws);
return 0;
}
@@ -137,12 +150,16 @@
* system got before the event was canceled
*/
+ trace_kgsl_fire_event(id, cur, jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, id, cur);
kgsl_context_put(context);
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
/* Remove ourselves from the master pending list */
@@ -175,6 +192,10 @@
* the callback knows how far the GPU made it before things went
* explosion
*/
+
+ trace_kgsl_fire_event(KGSL_MEMSTORE_GLOBAL, cur,
+ jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
cur);
@@ -184,6 +205,8 @@
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
}
EXPORT_SYMBOL(kgsl_cancel_events);
@@ -207,6 +230,9 @@
* to the timestamp they wanted
*/
+ trace_kgsl_fire_event(id, event->timestamp,
+ jiffies - event->created);
+
if (event->func)
event->func(device, event->priv, id, event->timestamp);
@@ -215,6 +241,8 @@
list_del(&event->list);
kfree(event);
+
+ kgsl_active_count_put(device);
}
}
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index f7818bb..8c4811e 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -693,6 +693,41 @@
)
);
+TRACE_EVENT(kgsl_register_event,
+ TP_PROTO(unsigned int id, unsigned int timestamp),
+ TP_ARGS(id, timestamp),
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(unsigned int, timestamp)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->timestamp = timestamp;
+ ),
+ TP_printk(
+ "ctx=%d ts=%d",
+ __entry->id, __entry->timestamp)
+);
+
+TRACE_EVENT(kgsl_fire_event,
+ TP_PROTO(unsigned int id, unsigned int ts,
+ unsigned int age),
+ TP_ARGS(id, ts, age),
+ TP_STRUCT__entry(
+ __field(unsigned int, id)
+ __field(unsigned int, ts)
+ __field(unsigned int, age)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->ts = ts;
+ __entry->age = age;
+ ),
+ TP_printk(
+ "ctx=%d ts=%d age=%u",
+ __entry->id, __entry->ts, __entry->age)
+);
+
#endif /* _KGSL_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 899c83b..2c79276 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -82,6 +82,8 @@
#define RMI4_I2C_LOAD_UA 10000
#define RMI4_I2C_LPM_LOAD_UA 10
+#define RMI4_GPIO_SLEEP_LOW_US 10000
+#define RMI4_GPIO_WAIT_HIGH_MS 25
static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr, unsigned char *data,
@@ -1871,15 +1873,60 @@
retval = synaptics_rmi4_regulator_configure(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to configure regulators\n");
- goto err_input_device;
+ goto err_reg_configure;
}
retval = synaptics_rmi4_power_on(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to power on\n");
- goto err_input_device;
+ goto err_power_device;
}
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ /* configure touchscreen irq gpio */
+ retval = gpio_request(platform_data->irq_gpio, "rmi4_irq_gpio");
+ if (retval) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_query_device;
+ }
+ retval = gpio_direction_input(platform_data->irq_gpio);
+ if (retval) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq_gpio_req;
+ }
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_query_device;
+ }
+
+ if (gpio_is_valid(platform_data->reset_gpio)) {
+ /* configure touchscreen reset out gpio */
+ retval = gpio_request(platform_data->reset_gpio,
+ "rmi4_reset_gpio");
+ if (retval) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->reset_gpio);
+ goto err_irq_gpio_req;
+ }
+
+ retval = gpio_direction_output(platform_data->reset_gpio, 1);
+ if (retval) {
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->reset_gpio);
+ goto err_reset_gpio_req;
+ }
+
+ gpio_set_value(platform_data->reset_gpio, 0);
+ usleep(RMI4_GPIO_SLEEP_LOW_US);
+ gpio_set_value(platform_data->reset_gpio, 1);
+ msleep(RMI4_GPIO_WAIT_HIGH_MS);
+ }
+
+
init_waitqueue_head(&rmi4_data->wait);
mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
@@ -1888,7 +1935,7 @@
dev_err(&client->dev,
"%s: Failed to query device\n",
__func__);
- goto err_query_device;
+ goto err_reset_gpio_req;
}
i2c_set_clientdata(client, rmi4_data);
@@ -1972,9 +2019,6 @@
input_unregister_device(rmi4_data->input_dev);
err_register_input:
-err_query_device:
- synaptics_rmi4_power_on(rmi4_data, false);
- synaptics_rmi4_regulator_configure(rmi4_data, false);
if (!list_empty(&rmi->support_fn_list)) {
list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
@@ -1984,6 +2028,17 @@
kfree(fhandler);
}
}
+err_reset_gpio_req:
+ if (gpio_is_valid(platform_data->reset_gpio))
+ gpio_free(platform_data->reset_gpio);
+err_irq_gpio_req:
+ if (gpio_is_valid(platform_data->irq_gpio))
+ gpio_free(platform_data->irq_gpio);
+err_query_device:
+ synaptics_rmi4_power_on(rmi4_data, false);
+err_power_device:
+ synaptics_rmi4_regulator_configure(rmi4_data, false);
+err_reg_configure:
input_free_device(rmi4_data->input_dev);
rmi4_data->input_dev = NULL;
err_input_device:
@@ -2036,7 +2091,11 @@
kfree(fhandler);
}
}
- input_free_device(rmi4_data->input_dev);
+
+ if (gpio_is_valid(rmi4_data->board->reset_gpio))
+ gpio_free(rmi4_data->board->reset_gpio);
+ if (gpio_is_valid(rmi4_data->board->irq_gpio))
+ gpio_free(rmi4_data->board->irq_gpio);
synaptics_rmi4_power_on(rmi4_data, false);
synaptics_rmi4_regulator_configure(rmi4_data, false);
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 2bbdc22..e4777e6 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -82,6 +82,15 @@
snapshot config = 4000 * 3000 at 20 fps,
hfr video at 60, 90 and 120 fps.
+config IMX135
+ bool "Sensor IMX135 (BAYER 12M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Sony 12 MP Bayer Sensor with auto focus, uses
+ 4 mipi lanes, preview config = 2104 x 1560 at 49 fps,
+ snapshot config = 4208 x 3120 at 24 fps,
+ Video HDR support.
+
config OV2720
bool "Sensor OV2720 (BAYER 2M)"
depends on MSMB_CAMERA
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 08a4566..6974cb4 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -461,9 +461,6 @@
static inline int __msm_sd_close_session_streams(struct v4l2_subdev *sd,
struct msm_sd_close_ioctl *sd_close)
{
- v4l2_subdev_call(sd, core, ioctl,
- MSM_SD_CLOSE_SESSION_AND_STREAM, &sd_close);
-
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 6ea86ae..22131f8 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -105,7 +105,7 @@
unsigned long flags;
stream = msm_get_stream(session_id, stream_id);
- if (!stream)
+ if (IS_ERR_OR_NULL(stream))
return NULL;
spin_lock_irqsave(&stream->stream_lock, flags);
@@ -163,7 +163,7 @@
int rc = 0;
stream = msm_get_stream(session_id, stream_id);
- if (!stream)
+ if (IS_ERR_OR_NULL(stream))
return 0;
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
@@ -172,6 +172,7 @@
/* put buf before buf done */
if (msm_vb2->in_freeq) {
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ msm_vb2->in_freeq = 0;
rc = 0;
} else
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index f6011ba..6f941f7 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -6,5 +6,6 @@
obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ flash/
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX135) += imx135.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_MT9M114) += mt9m114.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/imx135.c b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
new file mode 100644
index 0000000..c9476ee
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/imx135.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "msm_sensor.h"
+#define IMX135_SENSOR_NAME "imx135"
+DEFINE_MSM_MUTEX(imx135_mut);
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl;
+
+static struct msm_sensor_power_setting imx135_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 0,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct v4l2_subdev_info imx135_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id imx135_i2c_id[] = {
+ {IMX135_SENSOR_NAME, (kernel_ulong_t)&imx135_s_ctrl},
+ { }
+};
+
+static struct i2c_driver imx135_i2c_driver = {
+ .id_table = imx135_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = IMX135_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client imx135_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static const struct of_device_id imx135_dt_match[] = {
+ {.compatible = "qcom,imx135", .data = &imx135_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, imx135_dt_match);
+
+static struct platform_driver imx135_platform_driver = {
+ .driver = {
+ .name = "qcom,imx135",
+ .owner = THIS_MODULE,
+ .of_match_table = imx135_dt_match,
+ },
+};
+
+static int32_t imx135_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+ match = of_match_device(imx135_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init imx135_init_module(void)
+{
+ int32_t rc = 0;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&imx135_platform_driver,
+ imx135_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&imx135_i2c_driver);
+}
+
+static void __exit imx135_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (imx135_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&imx135_s_ctrl);
+ platform_driver_unregister(&imx135_platform_driver);
+ } else
+ i2c_del_driver(&imx135_i2c_driver);
+ return;
+}
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl = {
+ .sensor_i2c_client = &imx135_sensor_i2c_client,
+ .power_setting_array.power_setting = imx135_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(imx135_power_setting),
+ .msm_sensor_mutex = &imx135_mut,
+ .sensor_v4l2_subdev_info = imx135_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx135_subdev_info),
+};
+
+module_init(imx135_init_module);
+module_exit(imx135_exit_module);
+MODULE_DESCRIPTION("imx135");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 8593760..f23c0aa 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -199,9 +199,18 @@
case HAL_BUFFER_INTERNAL_SCRATCH:
buffer = HFI_BUFFER_INTERNAL_SCRATCH;
break;
+ case HAL_BUFFER_INTERNAL_SCRATCH_1:
+ buffer = HFI_BUFFER_INTERNAL_SCRATCH_1;
+ break;
+ case HAL_BUFFER_INTERNAL_SCRATCH_2:
+ buffer = HFI_BUFFER_INTERNAL_SCRATCH_2;
+ break;
case HAL_BUFFER_INTERNAL_PERSIST:
buffer = HFI_BUFFER_INTERNAL_PERSIST;
break;
+ case HAL_BUFFER_INTERNAL_PERSIST_1:
+ buffer = HFI_BUFFER_INTERNAL_PERSIST_1;
+ break;
default:
dprintk(VIDC_ERR, "Invalid buffer :0x%x\n",
hal_buffer);
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 709eafc..be9458d 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -391,12 +391,30 @@
buffreq->buffer[6].buffer_type =
HAL_BUFFER_INTERNAL_SCRATCH;
break;
- case HFI_BUFFER_INTERNAL_PERSIST:
+ case HFI_BUFFER_INTERNAL_SCRATCH_1:
memcpy(&buffreq->buffer[7], hfi_buf_req,
- sizeof(struct hfi_buffer_requirements));
+ sizeof(struct hfi_buffer_requirements));
buffreq->buffer[7].buffer_type =
+ HAL_BUFFER_INTERNAL_SCRATCH_1;
+ break;
+ case HFI_BUFFER_INTERNAL_SCRATCH_2:
+ memcpy(&buffreq->buffer[8], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[8].buffer_type =
+ HAL_BUFFER_INTERNAL_SCRATCH_2;
+ break;
+ case HFI_BUFFER_INTERNAL_PERSIST:
+ memcpy(&buffreq->buffer[9], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[9].buffer_type =
HAL_BUFFER_INTERNAL_PERSIST;
break;
+ case HFI_BUFFER_INTERNAL_PERSIST_1:
+ memcpy(&buffreq->buffer[10], hfi_buf_req,
+ sizeof(struct hfi_buffer_requirements));
+ buffreq->buffer[10].buffer_type =
+ HAL_BUFFER_INTERNAL_PERSIST_1;
+ break;
default:
dprintk(VIDC_ERR,
"hal_process_sess_get_prop_buf_req: bad_buffer_type: %d",
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index d43e5ba..8cce310 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1443,6 +1443,198 @@
return flipped_state;
}
+struct hal_buffer_requirements *get_buff_req_buffer(
+ struct msm_vidc_inst *inst, enum hal_buffer buffer_type)
+{
+ int i;
+ for (i = 0; i < HAL_BUFFER_MAX; i++) {
+ if (inst->buff_req.buffer[i].buffer_type == buffer_type)
+ return &inst->buff_req.buffer[i];
+ }
+ return NULL;
+}
+
+static int set_scratch_buffers(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type)
+{
+ int rc = 0;
+ struct msm_smem *handle;
+ struct internal_buf *binfo;
+ struct vidc_buffer_addr_info buffer_info;
+ u32 smem_flags = 0;
+ int domain;
+ struct hal_buffer_requirements *scratch_buf;
+ int i;
+ struct hfi_device *hdev;
+
+ hdev = inst->core->device;
+
+ scratch_buf = get_buff_req_buffer(inst, buffer_type);
+ if (!scratch_buf) {
+ dprintk(VIDC_DBG,
+ "This scratch buffer not required, buffer_type: %x\n",
+ buffer_type);
+ return 0;
+ }
+ dprintk(VIDC_DBG,
+ "scratch: num = %d, size = %d\n",
+ scratch_buf->buffer_count_actual,
+ scratch_buf->buffer_size);
+
+ if (inst->mode == VIDC_SECURE) {
+ domain = call_hfi_op(hdev, get_domain,
+ hdev->hfi_device_data, CP_MAP);
+ smem_flags |= SMEM_SECURE;
+ } else
+ domain = call_hfi_op(hdev, get_domain,
+ hdev->hfi_device_data, NS_MAP);
+
+ if (scratch_buf->buffer_size) {
+ for (i = 0; i < scratch_buf->buffer_count_actual;
+ i++) {
+ handle = msm_smem_alloc(inst->mem_client,
+ scratch_buf->buffer_size, 1, smem_flags,
+ domain, 0, 0);
+ if (!handle) {
+ dprintk(VIDC_ERR,
+ "Failed to allocate scratch memory\n");
+ rc = -ENOMEM;
+ goto err_no_mem;
+ }
+ rc = msm_smem_cache_operations(inst->mem_client,
+ handle, SMEM_CACHE_CLEAN);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to clean cache may cause undefined behavior\n");
+ }
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto fail_kzalloc;
+ }
+ binfo->handle = handle;
+ buffer_info.buffer_size = scratch_buf->buffer_size;
+ buffer_info.buffer_type = buffer_type;
+ binfo->buffer_type = buffer_type;
+ buffer_info.num_buffers = 1;
+ buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Scratch buffer address: %x",
+ buffer_info.align_device_addr);
+ rc = call_hfi_op(hdev, session_set_buffers,
+ (void *) inst->session, &buffer_info);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "vidc_hal_session_set_buffers failed");
+ goto fail_set_buffers;
+ }
+ mutex_lock(&inst->lock);
+ list_add_tail(&binfo->list, &inst->internalbufs);
+ mutex_unlock(&inst->lock);
+ }
+ }
+ return rc;
+fail_set_buffers:
+ kfree(binfo);
+fail_kzalloc:
+ msm_smem_free(inst->mem_client, handle);
+err_no_mem:
+ return rc;
+}
+
+static int set_persist_buffers(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type)
+{
+ int rc = 0;
+ struct msm_smem *handle;
+ struct internal_buf *binfo;
+ struct vidc_buffer_addr_info buffer_info;
+ u32 smem_flags = 0;
+ int domain;
+ struct hal_buffer_requirements *persist_buf;
+ int i;
+ struct hfi_device *hdev;
+
+ hdev = inst->core->device;
+
+ persist_buf = get_buff_req_buffer(inst, buffer_type);
+ if (!persist_buf) {
+ dprintk(VIDC_DBG,
+ "This persist buffer not required, buffer_type: %x\n",
+ buffer_type);
+ return 0;
+ }
+
+ dprintk(VIDC_DBG,
+ "persist: num = %d, size = %d\n",
+ persist_buf->buffer_count_actual,
+ persist_buf->buffer_size);
+ if (!list_empty(&inst->persistbufs)) {
+ dprintk(VIDC_ERR,
+ "Persist buffers already allocated\n");
+ return rc;
+ }
+
+ if (inst->mode == VIDC_SECURE) {
+ domain = call_hfi_op(hdev, get_domain,
+ hdev->hfi_device_data, CP_MAP);
+ smem_flags |= SMEM_SECURE;
+ } else
+ domain = call_hfi_op(hdev, get_domain,
+ hdev->hfi_device_data, NS_MAP);
+
+ if (persist_buf->buffer_size) {
+ for (i = 0; i < persist_buf->buffer_count_actual; i++) {
+ handle = msm_smem_alloc(inst->mem_client,
+ persist_buf->buffer_size, 1, smem_flags,
+ domain, 0, 0);
+ if (!handle) {
+ dprintk(VIDC_ERR,
+ "Failed to allocate persist memory\n");
+ rc = -ENOMEM;
+ goto err_no_mem;
+ }
+ rc = msm_smem_cache_operations(inst->mem_client,
+ handle, SMEM_CACHE_CLEAN);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to clean cache may cause undefined behavior\n");
+ }
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto fail_kzalloc;
+ }
+ binfo->handle = handle;
+ buffer_info.buffer_size = persist_buf->buffer_size;
+ buffer_info.buffer_type = buffer_type;
+ binfo->buffer_type = buffer_type;
+ buffer_info.num_buffers = 1;
+ buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Persist buffer address: %x",
+ buffer_info.align_device_addr);
+ rc = call_hfi_op(hdev, session_set_buffers,
+ (void *) inst->session, &buffer_info);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "vidc_hal_session_set_buffers failed");
+ goto fail_set_buffers;
+ }
+ mutex_lock(&inst->lock);
+ list_add_tail(&binfo->list, &inst->persistbufs);
+ mutex_unlock(&inst->lock);
+ }
+ }
+ return rc;
+fail_set_buffers:
+ kfree(binfo);
+fail_kzalloc:
+ msm_smem_free(inst->mem_client, handle);
+err_no_mem:
+ return rc;
+}
+
int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
{
int rc = 0;
@@ -1723,6 +1915,7 @@
mutex_unlock(&inst->sync_lock);
return rc;
}
+
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
@@ -1755,7 +1948,7 @@
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
+ buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
@@ -1819,7 +2012,7 @@
list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
+ buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
@@ -1885,178 +2078,50 @@
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
- struct msm_smem *handle;
- struct internal_buf *binfo;
- struct vidc_buffer_addr_info buffer_info;
- int domain;
- unsigned long smem_flags = 0;
- struct hal_buffer_requirements *scratch_buf;
- int i;
- struct hfi_device *hdev;
-
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
-
- scratch_buf =
- &inst->buff_req.buffer[HAL_BUFFER_INTERNAL_SCRATCH];
- dprintk(VIDC_DBG,
- "scratch: num = %d, size = %d\n",
- scratch_buf->buffer_count_actual,
- scratch_buf->buffer_size);
if (msm_comm_release_scratch_buffers(inst))
dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
- if (inst->mode == VIDC_SECURE) {
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, CP_MAP);
- smem_flags |= SMEM_SECURE;
- } else
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, NS_MAP);
- if (scratch_buf->buffer_size) {
- for (i = 0; i < scratch_buf->buffer_count_actual;
- i++) {
- handle = msm_smem_alloc(inst->mem_client,
- scratch_buf->buffer_size, 1, smem_flags,
- domain, 0, 0);
- if (!handle) {
- dprintk(VIDC_ERR,
- "Failed to allocate scratch memory\n");
- rc = -ENOMEM;
- goto err_no_mem;
- }
- rc = msm_smem_cache_operations(inst->mem_client,
- handle, SMEM_CACHE_CLEAN);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed to clean cache may cause undefined behavior\n");
- }
- binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
- if (!binfo) {
- dprintk(VIDC_ERR, "Out of memory\n");
- rc = -ENOMEM;
- goto fail_kzalloc;
- }
- binfo->handle = handle;
- buffer_info.buffer_size = scratch_buf->buffer_size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
- buffer_info.num_buffers = 1;
- buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Scratch buffer address: %x",
- buffer_info.align_device_addr);
- rc = call_hfi_op(hdev, session_set_buffers,
- (void *) inst->session, &buffer_info);
- if (rc) {
- dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
- goto fail_set_buffers;
- }
- mutex_lock(&inst->lock);
- list_add_tail(&binfo->list, &inst->internalbufs);
- mutex_unlock(&inst->lock);
- }
- }
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH);
+ if (rc)
+ goto error;
+
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1);
+ if (rc)
+ goto error;
+
+ rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2);
+ if (rc)
+ goto error;
+
return rc;
-fail_set_buffers:
- kfree(binfo);
-fail_kzalloc:
- msm_smem_free(inst->mem_client, handle);
-err_no_mem:
+error:
+ msm_comm_release_scratch_buffers(inst);
return rc;
}
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
- struct msm_smem *handle;
- struct internal_buf *binfo;
- struct vidc_buffer_addr_info buffer_info;
- unsigned long flags;
- unsigned long smem_flags = 0;
- int domain;
- struct hal_buffer_requirements *persist_buf;
- int i;
- struct hfi_device *hdev;
-
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
+ rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST);
+ if (rc)
+ goto error;
- persist_buf =
- &inst->buff_req.buffer[HAL_BUFFER_INTERNAL_PERSIST];
- dprintk(VIDC_DBG,
- "persist: num = %d, size = %d\n",
- persist_buf->buffer_count_actual,
- persist_buf->buffer_size);
- if (!list_empty(&inst->persistbufs)) {
- dprintk(VIDC_ERR,
- "Persist buffers already allocated\n");
- return rc;
- }
-
- if (inst->mode == VIDC_SECURE) {
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, CP_MAP);
- flags |= SMEM_SECURE;
- } else
- domain = call_hfi_op(hdev, get_domain,
- hdev->hfi_device_data, NS_MAP);
-
- if (persist_buf->buffer_size) {
- for (i = 0; i < persist_buf->buffer_count_actual; i++) {
- handle = msm_smem_alloc(inst->mem_client,
- persist_buf->buffer_size, 1, smem_flags,
- domain, 0, 0);
- if (!handle) {
- dprintk(VIDC_ERR,
- "Failed to allocate persist memory\n");
- rc = -ENOMEM;
- goto err_no_mem;
- }
- rc = msm_smem_cache_operations(inst->mem_client,
- handle, SMEM_CACHE_CLEAN);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed to clean cache may cause undefined behavior\n");
- }
- binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
- if (!binfo) {
- dprintk(VIDC_ERR, "Out of memory\n");
- rc = -ENOMEM;
- goto fail_kzalloc;
- }
- binfo->handle = handle;
- buffer_info.buffer_size = persist_buf->buffer_size;
- buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
- buffer_info.num_buffers = 1;
- buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Persist buffer address: %x",
- buffer_info.align_device_addr);
- rc = call_hfi_op(hdev, session_set_buffers,
- (void *) inst->session, &buffer_info);
- if (rc) {
- dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
- goto fail_set_buffers;
- }
- mutex_lock(&inst->lock);
- list_add_tail(&binfo->list, &inst->persistbufs);
- mutex_unlock(&inst->lock);
- }
- }
+ rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1);
+ if (rc)
+ goto error;
return rc;
-fail_set_buffers:
- kfree(binfo);
-fail_kzalloc:
- msm_smem_free(inst->mem_client, handle);
-err_no_mem:
+error:
+ msm_comm_release_persist_buffers(inst);
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index c03a4c4..8238d42 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -92,6 +92,7 @@
struct internal_buf {
struct list_head list;
+ enum hal_buffer buffer_type;
struct msm_smem *handle;
};
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 75594b3..8b3e7cb 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -53,6 +53,8 @@
#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5)
+#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6)
#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index d06ea51..a057303 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -383,7 +383,10 @@
HAL_BUFFER_EXTRADATA_OUTPUT,
HAL_BUFFER_EXTRADATA_OUTPUT2,
HAL_BUFFER_INTERNAL_SCRATCH,
+ HAL_BUFFER_INTERNAL_SCRATCH_1,
+ HAL_BUFFER_INTERNAL_SCRATCH_2,
HAL_BUFFER_INTERNAL_PERSIST,
+ HAL_BUFFER_INTERNAL_PERSIST_1,
HAL_BUFFER_MAX
};
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 37c051e..01c5e0b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -179,6 +179,7 @@
#define HFI_BUFFER_OUTPUT (HFI_COMMON_BASE + 0x2)
#define HFI_BUFFER_OUTPUT2 (HFI_COMMON_BASE + 0x3)
#define HFI_BUFFER_INTERNAL_PERSIST (HFI_COMMON_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_PERSIST_1 (HFI_COMMON_BASE + 0x5)
struct hfi_buffer_info {
u32 buffer_addr;
diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h
index 0c93ef2..c6c8fc9 100644
--- a/drivers/misc/qseecom_kernel.h
+++ b/drivers/misc/qseecom_kernel.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,12 @@
#define __QSEECOM_KERNEL_H_
#include <linux/types.h>
+
+#define QSEECOM_ALIGN_SIZE 0x40
+#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1)
+#define QSEECOM_ALIGN(x) \
+ ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))
+
/*
* struct qseecom_handle -
* Handle to the qseecom device for kernel clients
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index a25c799..b7eca61 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
- ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o \
+ ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o ipa_intf.o teth_bridge.o \
ipa_rm.o ipa_rm_dependency_graph.o ipa_rm_peers_list.o ipa_rm_resource.o ipa_rm_inactivity_timer.o
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
index 60cb6bd..4b5f0a2 100644
--- a/drivers/platform/msm/ipa/a2_service.c
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -1534,6 +1534,13 @@
}
if (smsm_get_state(SMSM_MODEM_STATE) & SMSM_A2_POWER_CONTROL)
bam_dmux_smsm_cb(NULL, 0, smsm_get_state(SMSM_MODEM_STATE));
+
+ /*
+ * Set remote channel open for tethered channel since there is
+ * no actual remote tethered channel
+ */
+ a2_mux_ctx->bam_ch[A2_MUX_TETHERED_0].status |= BAM_CH_REMOTE_OPEN;
+
rc = 0;
goto bail;
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index b8e0ce7..edf3a60 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -1900,10 +1900,6 @@
ipa_ctx->aggregation_byte_limit = 1;
ipa_ctx->aggregation_time_limit = 0;
- /* gate IPA clocks */
- if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
- ipa_disable_clks();
-
/* Initialize IPA RM (resource manager) */
result = ipa_rm_initialize();
if (result) {
@@ -1914,6 +1910,18 @@
a2_mux_init();
+ /* Initialize the tethering bridge driver */
+ result = teth_bridge_driver_init();
+ if (result) {
+ IPAERR(":teth_bridge_driver_init() failed\n");
+ result = -ENODEV;
+ goto fail_cdev_add;
+ }
+
+ /* gate IPA clocks */
+ if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_NORMAL)
+ ipa_disable_clks();
+
IPADBG(":IPA driver init OK.\n");
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
index a7d1efc..cb8c0f5 100644
--- a/drivers/platform/msm/ipa/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -825,5 +825,6 @@
void wwan_cleanup(void);
+int teth_bridge_driver_init(void);
#endif /* _IPA_I_H_ */
diff --git a/drivers/platform/msm/ipa/teth_bridge.c b/drivers/platform/msm/ipa/teth_bridge.c
new file mode 100644
index 0000000..76e2eee
--- /dev/null
+++ b/drivers/platform/msm/ipa/teth_bridge.c
@@ -0,0 +1,1483 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/completion.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/if_ether.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msm_ipa.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <mach/bam_dmux.h>
+#include <mach/ipa.h>
+#include <mach/sps.h>
+#include "ipa_i.h"
+
+#define TETH_BRIDGE_DRV_NAME "ipa_tethering_bridge"
+
+#ifdef TETH_DEBUG
+#define TETH_DBG(fmt, args...) \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args)
+#define TETH_DBG_FUNC_ENTRY() \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d ENTRY\n", __func__, __LINE__)
+#define TETH_DBG_FUNC_EXIT() \
+ pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d EXIT\n", __func__, __LINE__)
+#else
+#define TETH_DBG(fmt, args...)
+#define TETH_DBG_FUNC_ENTRY()
+#define TETH_DBG_FUNC_EXIT()
+#endif
+
+#define TETH_ERR(fmt, args...) \
+ pr_err(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+
+#define USB_ETH_HDR_NAME_IPV4 "usb_bridge_ipv4"
+#define USB_ETH_HDR_NAME_IPV6 "usb_bridge_ipv6"
+#define A2_ETH_HDR_NAME_IPV4 "a2_bridge_ipv4"
+#define A2_ETH_HDR_NAME_IPV6 "a2_bridge_ipv6"
+
+#define USB_TO_A2_RT_TBL_NAME_IPV4 "usb_a2_rt_tbl_ipv4"
+#define A2_TO_USB_RT_TBL_NAME_IPV4 "a2_usb_rt_tbl_ipv4"
+#define USB_TO_A2_RT_TBL_NAME_IPV6 "usb_a2_rt_tbl_ipv6"
+#define A2_TO_USB_RT_TBL_NAME_IPV6 "a2_usb_rt_tbl_ipv6"
+
+#define MBIM_HEADER_NAME "mbim_header"
+#define TETH_DEFAULT_AGGR_TIME_LIMIT 1
+
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_IPV6 0x86DD
+
+struct mac_addresses_type {
+ u8 host_pc_mac_addr[ETH_ALEN];
+ bool host_pc_mac_addr_known;
+ u8 device_mac_addr[ETH_ALEN];
+ bool device_mac_addr_known;
+};
+
+struct teth_bridge_ctx {
+ struct class *class;
+ dev_t dev_num;
+ struct device *dev;
+ struct cdev cdev;
+ u32 usb_ipa_pipe_hdl;
+ u32 ipa_usb_pipe_hdl;
+ u32 a2_ipa_pipe_hdl;
+ u32 ipa_a2_pipe_hdl;
+ bool is_connected;
+ enum teth_link_protocol_type link_protocol;
+ struct mac_addresses_type mac_addresses;
+ bool is_hw_bridge_complete;
+ struct teth_aggr_params aggr_params;
+ bool aggr_params_known;
+ enum teth_tethering_mode tethering_mode;
+ struct completion is_bridge_prod_up;
+ struct completion is_bridge_prod_down;
+ struct work_struct comp_hw_bridge_work;
+ bool comp_hw_bridge_in_progress;
+ struct teth_aggr_capabilities *aggr_caps;
+};
+
+static struct teth_bridge_ctx *teth_ctx;
+
+#ifdef CONFIG_DEBUG_FS
+#define TETH_MAX_MSG_LEN 512
+static char dbg_buff[TETH_MAX_MSG_LEN];
+#endif
+
+static int add_eth_hdrs(char *hdr_name_ipv4, char *hdr_name_ipv6,
+ u8 *src_mac_addr, u8 *dst_mac_addr)
+{
+ int res;
+ struct ipa_ioc_add_hdr *hdrs;
+ struct ethhdr hdr_ipv4;
+ struct ethhdr hdr_ipv6;
+
+ TETH_DBG_FUNC_ENTRY();
+ memcpy(hdr_ipv4.h_source, src_mac_addr, ETH_ALEN);
+ memcpy(hdr_ipv4.h_dest, dst_mac_addr, ETH_ALEN);
+ hdr_ipv4.h_proto = htons(ETHERTYPE_IPV4);
+
+ memcpy(hdr_ipv6.h_source, src_mac_addr, ETH_ALEN);
+ memcpy(hdr_ipv6.h_dest, dst_mac_addr, ETH_ALEN);
+ hdr_ipv6.h_proto = htons(ETHERTYPE_IPV6);
+
+ /* Add headers to the header insertion tables */
+ hdrs = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
+ if (hdrs == NULL) {
+ TETH_ERR("Failed allocating memory for headers !\n");
+ return -ENOMEM;
+ }
+
+ hdrs->commit = 0;
+ hdrs->num_hdrs = 2;
+
+ /* Ethernet IPv4 header */
+ strlcpy(hdrs->hdr[0].name, hdr_name_ipv4, IPA_RESOURCE_NAME_MAX);
+ hdrs->hdr[0].hdr_len = ETH_HLEN;
+ memcpy(hdrs->hdr[0].hdr, &hdr_ipv4, ETH_HLEN);
+
+ /* Ethernet IPv6 header */
+ strlcpy(hdrs->hdr[1].name, hdr_name_ipv6, IPA_RESOURCE_NAME_MAX);
+ hdrs->hdr[1].hdr_len = ETH_HLEN;
+ memcpy(hdrs->hdr[1].hdr, &hdr_ipv6, ETH_HLEN);
+
+ res = ipa_add_hdr(hdrs);
+ if (res || hdrs->hdr[0].status || hdrs->hdr[1].status)
+ TETH_ERR("Header insertion failed\n");
+ kfree(hdrs);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_ipa_header_block_internal(u32 usb_ipa_hdr_len,
+ u32 a2_ipa_hdr_len,
+ u32 ipa_usb_hdr_len,
+ u32 ipa_a2_hdr_len)
+{
+ struct ipa_ep_cfg_hdr hdr_cfg;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Configure header removal for the USB->IPA pipe and A2->IPA pipe */
+ memset(&hdr_cfg, 0, sizeof(hdr_cfg));
+ hdr_cfg.hdr_len = usb_ipa_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->usb_ipa_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header removal config for USB->IPA pipe failed\n");
+ goto bail;
+ }
+
+ hdr_cfg.hdr_len = a2_ipa_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->a2_ipa_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header removal config for A2->IPA pipe failed\n");
+ goto bail;
+ }
+
+ /* Configure header insertion for the IPA->USB pipe and IPA->A2 pipe */
+ hdr_cfg.hdr_len = ipa_usb_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->ipa_usb_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header insertion config for IPA->USB pipe failed\n");
+ goto bail;
+ }
+
+ hdr_cfg.hdr_len = ipa_a2_hdr_len;
+ res = ipa_cfg_ep_hdr(teth_ctx->ipa_a2_pipe_hdl, &hdr_cfg);
+ if (res) {
+ TETH_ERR("Header insertion config for IPA->A2 pipe failed\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int add_mbim_hdr(void)
+{
+ int res;
+ struct ipa_ioc_add_hdr *mbim_hdr;
+ u8 mbim_stream_id = 0;
+
+ TETH_DBG_FUNC_ENTRY();
+ mbim_hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ sizeof(struct ipa_hdr_add),
+ GFP_KERNEL);
+ if (!mbim_hdr) {
+ TETH_ERR("Failed allocating memory for MBIM header\n");
+ return -ENOMEM;
+ }
+
+ mbim_hdr->commit = 0;
+ mbim_hdr->num_hdrs = 1;
+ strlcpy(mbim_hdr->hdr[0].name, MBIM_HEADER_NAME, IPA_RESOURCE_NAME_MAX);
+ memcpy(mbim_hdr->hdr[0].hdr, &mbim_stream_id, sizeof(u8));
+ mbim_hdr->hdr[0].hdr_len = sizeof(u8);
+ mbim_hdr->hdr[0].is_partial = false;
+ res = ipa_add_hdr(mbim_hdr);
+ if (res || mbim_hdr->hdr[0].status) {
+ TETH_ERR("Failed adding MBIM header\n");
+ res = -EFAULT;
+ } else {
+ TETH_DBG("Added MBIM stream ID header\n");
+ }
+ kfree(mbim_hdr);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_ipa_header_block(void)
+{
+ int res;
+ u32 hdr_len = 0;
+ u32 ipa_usb_hdr_len = 0;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) {
+ /*
+ * Create a new header for MBIM stream ID and associate it with
+ * the IPA->USB routing table
+ */
+ if (teth_ctx->aggr_params.dl.aggr_prot ==
+ TETH_AGGR_PROTOCOL_MBIM) {
+ ipa_usb_hdr_len = 1;
+ res = add_mbim_hdr();
+ if (res) {
+ TETH_ERR("Failed adding MBIM header\n");
+ goto bail;
+ }
+ }
+ } else if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ /* Add a header entry for USB */
+ res = add_eth_hdrs(USB_ETH_HDR_NAME_IPV4,
+ USB_ETH_HDR_NAME_IPV6,
+ teth_ctx->mac_addresses.host_pc_mac_addr,
+ teth_ctx->mac_addresses.device_mac_addr);
+ if (res) {
+ TETH_ERR("Failed adding USB Ethernet header\n");
+ goto bail;
+ }
+ TETH_DBG("Added USB Ethernet headers (IPv4 / IPv6)\n");
+
+ /* Add a header entry for A2 */
+ res = add_eth_hdrs(A2_ETH_HDR_NAME_IPV4,
+ A2_ETH_HDR_NAME_IPV6,
+ teth_ctx->mac_addresses.device_mac_addr,
+ teth_ctx->mac_addresses.host_pc_mac_addr);
+ if (res) {
+ TETH_ERR("Failed adding A2 Ethernet header\n");
+ goto bail;
+ }
+ TETH_DBG("Added A2 Ethernet headers (IPv4 / IPv6\n");
+
+ hdr_len = ETH_HLEN;
+ ipa_usb_hdr_len = ETH_HLEN;
+ }
+
+ res = configure_ipa_header_block_internal(hdr_len,
+ hdr_len,
+ ipa_usb_hdr_len,
+ hdr_len);
+ if (res) {
+ TETH_ERR("Configuration of header removal/insertion failed\n");
+ goto bail;
+ }
+
+ res = ipa_commit_hdr();
+ if (res) {
+ TETH_ERR("Failed committing headers\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_routing_by_ip(char *hdr_name,
+ char *rt_tbl_name,
+ enum ipa_client_type dst,
+ enum ipa_ip_type ip_address_family)
+{
+
+ struct ipa_ioc_add_rt_rule *rt_rule;
+ struct ipa_ioc_get_hdr hdr_info;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Get the header handle */
+ memset(&hdr_info, 0, sizeof(hdr_info));
+ strlcpy(hdr_info.name, hdr_name, IPA_RESOURCE_NAME_MAX);
+ ipa_get_hdr(&hdr_info);
+
+ rt_rule = kzalloc(sizeof(struct ipa_ioc_add_rt_rule) +
+ 1 * sizeof(struct ipa_rt_rule_add),
+ GFP_KERNEL);
+ if (!rt_rule) {
+ TETH_ERR("Memory allocation failure");
+ return -ENOMEM;
+ }
+
+ /* Match all, do not commit to HW*/
+ rt_rule->commit = 0;
+ rt_rule->num_rules = 1;
+ rt_rule->ip = ip_address_family;
+ strlcpy(rt_rule->rt_tbl_name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
+ rt_rule->rules[0].rule.dst = dst;
+ rt_rule->rules[0].rule.hdr_hdl = hdr_info.hdl;
+ rt_rule->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
+ res = ipa_add_rt_rule(rt_rule);
+ if (res || rt_rule->rules[0].status)
+ TETH_ERR("Failed adding routing rule\n");
+ kfree(rt_rule);
+ TETH_DBG_FUNC_EXIT();
+
+ return res;
+}
+
+static int configure_routing(char *hdr_name_ipv4,
+ char *rt_tbl_name_ipv4,
+ char *hdr_name_ipv6,
+ char *rt_tbl_name_ipv6,
+ enum ipa_client_type dst)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Configure IPv4 routing table */
+ res = configure_routing_by_ip(hdr_name_ipv4,
+ rt_tbl_name_ipv4,
+ dst,
+ IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 routing table\n");
+ goto bail;
+ }
+
+ /* Configure IPv6 routing table */
+ res = configure_routing_by_ip(hdr_name_ipv6,
+ rt_tbl_name_ipv6,
+ dst,
+ IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed adding IPv6 routing table\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_ipa_routing_block(void)
+{
+ int res;
+ char hdr_name_ipv4[IPA_RESOURCE_NAME_MAX];
+ char hdr_name_ipv6[IPA_RESOURCE_NAME_MAX];
+
+ TETH_DBG_FUNC_ENTRY();
+ hdr_name_ipv4[0] = '\0';
+ hdr_name_ipv6[0] = '\0';
+
+ /* Configure USB -> A2 routing table */
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ strlcpy(hdr_name_ipv4,
+ A2_ETH_HDR_NAME_IPV4,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ A2_ETH_HDR_NAME_IPV6,
+ IPA_RESOURCE_NAME_MAX);
+ }
+ res = configure_routing(hdr_name_ipv4,
+ USB_TO_A2_RT_TBL_NAME_IPV4,
+ hdr_name_ipv6,
+ USB_TO_A2_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_A2_TETHERED_CONS);
+ if (res) {
+ TETH_ERR("USB to A2 routing block configuration failed\n");
+ goto bail;
+ }
+
+ /* Configure A2 -> USB routing table */
+ if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
+ strlcpy(hdr_name_ipv4,
+ USB_ETH_HDR_NAME_IPV4,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ USB_ETH_HDR_NAME_IPV6,
+ IPA_RESOURCE_NAME_MAX);
+ } else if (teth_ctx->aggr_params.dl.aggr_prot ==
+ TETH_AGGR_PROTOCOL_MBIM) {
+ strlcpy(hdr_name_ipv4,
+ MBIM_HEADER_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ strlcpy(hdr_name_ipv6,
+ MBIM_HEADER_NAME,
+ IPA_RESOURCE_NAME_MAX);
+ }
+ res = configure_routing(hdr_name_ipv4,
+ A2_TO_USB_RT_TBL_NAME_IPV4,
+ hdr_name_ipv6,
+ A2_TO_USB_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_USB_CONS);
+ if (res) {
+ TETH_ERR("A2 to USB routing block configuration failed\n");
+ goto bail;
+ }
+
+ /* Commit all the changes to HW in one shot */
+ res = ipa_commit_rt(IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed commiting IPv4 routing tables\n");
+ goto bail;
+ }
+ res = ipa_commit_rt(IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed commiting IPv6 routing tables\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_filtering_by_ip(char *rt_tbl_name,
+ enum ipa_client_type src,
+ enum ipa_ip_type ip_address_family)
+{
+ struct ipa_ioc_add_flt_rule *flt_tbl;
+ struct ipa_ioc_get_rt_tbl rt_tbl_info;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Get the needed routing table handle */
+ rt_tbl_info.ip = ip_address_family;
+ strlcpy(rt_tbl_info.name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
+ res = ipa_get_rt_tbl(&rt_tbl_info);
+ if (res) {
+ TETH_ERR("Failed getting routing table handle\n");
+ goto bail;
+ }
+
+ flt_tbl = kzalloc(sizeof(struct ipa_ioc_add_flt_rule) +
+ 1 * sizeof(struct ipa_flt_rule_add), GFP_KERNEL);
+ if (!flt_tbl) {
+ TETH_ERR("Filtering table memory allocation failure\n");
+ return -ENOMEM;
+ }
+
+ flt_tbl->commit = 0;
+ flt_tbl->ep = src;
+ flt_tbl->global = 0;
+ flt_tbl->ip = ip_address_family;
+ flt_tbl->num_rules = 1;
+ flt_tbl->rules[0].rule.action = IPA_PASS_TO_ROUTING;
+ flt_tbl->rules[0].rule.rt_tbl_hdl = rt_tbl_info.hdl;
+ flt_tbl->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
+
+ res = ipa_add_flt_rule(flt_tbl);
+ if (res || flt_tbl->rules[0].status)
+ TETH_ERR("Failed adding filtering table\n");
+ kfree(flt_tbl);
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_filtering(char *rt_tbl_name_ipv4,
+ char *rt_tbl_name_ipv6,
+ enum ipa_client_type src)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ res = configure_filtering_by_ip(rt_tbl_name_ipv4, src, IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 filtering table\n");
+ goto bail;
+ }
+
+ res = configure_filtering_by_ip(rt_tbl_name_ipv6, src, IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed adding IPv4 filtering table\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int configure_ipa_filtering_block(void)
+{
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ /* Filter all traffic coming from USB to A2 */
+ res = configure_filtering(USB_TO_A2_RT_TBL_NAME_IPV4,
+ USB_TO_A2_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_USB_PROD);
+ if (res) {
+ TETH_ERR("USB_PROD ep filtering configuration failed\n");
+ goto bail;
+ }
+
+ /* Filter all traffic coming from A2 to USB */
+ res = configure_filtering(A2_TO_USB_RT_TBL_NAME_IPV4,
+ A2_TO_USB_RT_TBL_NAME_IPV6,
+ IPA_CLIENT_A2_TETHERED_PROD);
+ if (res) {
+ TETH_ERR("A2_PROD filtering configuration failed\n");
+ goto bail;
+ }
+
+ /* Commit all the changes to HW in one shot */
+ res = ipa_commit_flt(IPA_IP_v4);
+ if (res) {
+ TETH_ERR("Failed commiting IPv4 filtering tables\n");
+ goto bail;
+ }
+ res = ipa_commit_flt(IPA_IP_v6);
+ if (res) {
+ TETH_ERR("Failed commiting IPv6 filtering tables\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static int prepare_ipa_aggr_struct(
+ const struct teth_aggr_params_link *teth_aggr_params,
+ struct ipa_ep_cfg_aggr *ipa_aggr_params,
+ bool client_is_prod)
+{
+ TETH_DBG_FUNC_ENTRY();
+ memset(ipa_aggr_params, 0, sizeof(*ipa_aggr_params));
+
+ switch (teth_aggr_params->aggr_prot) {
+ case TETH_AGGR_PROTOCOL_NONE:
+ ipa_aggr_params->aggr_en = IPA_BYPASS_AGGR;
+ break;
+ case TETH_AGGR_PROTOCOL_MBIM:
+ ipa_aggr_params->aggr = IPA_MBIM_16;
+ ipa_aggr_params->aggr_en = (client_is_prod) ?
+ IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
+ break;
+ case TETH_AGGR_PROTOCOL_TLP:
+ ipa_aggr_params->aggr = IPA_TLP;
+ ipa_aggr_params->aggr_en = (client_is_prod) ?
+ IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
+ break;
+ default:
+ TETH_ERR("Unsupported aggregation protocol\n");
+ return -EFAULT;
+ }
+
+ ipa_aggr_params->aggr_byte_limit =
+ teth_aggr_params->max_transfer_size_byte / 1024;
+ ipa_aggr_params->aggr_time_limit = TETH_DEFAULT_AGGR_TIME_LIMIT;
+ TETH_DBG_FUNC_EXIT();
+
+ return 0;
+}
+
+static int teth_set_aggr_per_ep(
+ const struct teth_aggr_params_link *teth_aggr_params,
+ bool client_is_prod,
+ u32 pipe_hdl)
+{
+ struct ipa_ep_cfg_aggr agg_params;
+ struct ipa_ep_cfg_hdr hdr_params;
+ int res;
+
+ TETH_DBG_FUNC_ENTRY();
+ res = prepare_ipa_aggr_struct(teth_aggr_params,
+ &agg_params,
+ client_is_prod);
+ if (res) {
+ TETH_ERR("prepare_ipa_aggregation_struct() failed\n");
+ goto bail;
+ }
+
+ res = ipa_cfg_ep_aggr(pipe_hdl, &agg_params);
+ if (res) {
+ TETH_ERR("ipa_cfg_ep_aggr() failed\n");
+ goto bail;
+ }
+
+ if (!client_is_prod) {
+ memset(&hdr_params, 0, sizeof(hdr_params));
+ hdr_params.hdr_len = 1;
+ res = ipa_cfg_ep_hdr(pipe_hdl, &hdr_params);
+ if (res) {
+ TETH_ERR("ipa_cfg_ep_hdr() failed\n");
+ goto bail;
+ }
+ }
+ TETH_DBG_FUNC_EXIT();
+
+bail:
+ return res;
+}
+
+static void aggr_prot_to_str(enum teth_aggr_protocol_type aggr_prot,
+ char *buff,
+ uint buff_size)
+{
+ switch (aggr_prot) {
+ case TETH_AGGR_PROTOCOL_NONE:
+ strlcpy(buff, "NONE", buff_size);
+ break;
+ case TETH_AGGR_PROTOCOL_MBIM:
+ strlcpy(buff, "MBIM", buff_size);
+ break;
+ case TETH_AGGR_PROTOCOL_TLP:
+ strlcpy(buff, "TLP", buff_size);
+ break;
+ default:
+ strlcpy(buff, "ERROR", buff_size);
+ break;
+ }
+}
+
+static int teth_set_aggregation(void)
+{
+ int res;
+ char aggr_prot_str[20];
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->aggr_params.ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
+ teth_ctx->aggr_params.dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM) {
+ res = ipa_set_aggr_mode(IPA_MBIM);
+ if (res) {
+ TETH_ERR("ipa_set_aggr_mode() failed\n");
+ goto bail;
+ }
+ res = ipa_set_single_ndp_per_mbim(false);
+ if (res) {
+ TETH_ERR("ipa_set_single_ndp_per_mbim() failed\n");
+ goto bail;
+ }
+ }
+
+ aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
+ aggr_prot_str,
+ sizeof(aggr_prot_str)-1);
+ TETH_DBG("Setting %s aggregation on UL\n", aggr_prot_str);
+ aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
+ aggr_prot_str,
+ sizeof(aggr_prot_str)-1);
+ TETH_DBG("Setting %s aggregation on DL\n", aggr_prot_str);
+
+ /* Configure aggregation on UL producer (USB->IPA) */
+ res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.ul,
+ true,
+ teth_ctx->usb_ipa_pipe_hdl);
+ if (res) {
+ TETH_ERR("teth_set_aggregation_per_ep() failed\n");
+ goto bail;
+ }
+
+ /* Configure aggregation on DL consumer (IPA->USB) */
+ res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.dl,
+ false,
+ teth_ctx->ipa_usb_pipe_hdl);
+ if (res) {
+ TETH_ERR("teth_set_aggregation_per_ep() failed\n");
+ goto bail;
+ }
+ TETH_DBG_FUNC_EXIT();
+bail:
+ return res;
+}
+
+static void complete_hw_bridge(struct work_struct *work)
+{
+ int res;
+ static DEFINE_MUTEX(f_lock);
+
+ mutex_lock(&f_lock);
+
+ TETH_DBG_FUNC_ENTRY();
+ TETH_DBG("Completing HW bridge in %s mode\n",
+ (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) ?
+ "ETHERNET" :
+ "IP");
+
+ res = teth_set_aggregation();
+ if (res) {
+ TETH_ERR("Failed setting aggregation params\n");
+ goto bail;
+ }
+
+ /*
+ * Reset the Header, Routing and Filtering blocks.
+ * Resetting the Header block will also reset the other blocks.
+ * This reset is not comitted to HW.
+ */
+ res = ipa_reset_hdr();
+ if (res) {
+ TETH_ERR("Failed resetting IPA\n");
+ goto bail;
+ }
+
+ res = configure_ipa_header_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA header block Failed\n");
+ goto bail;
+ }
+
+ res = configure_ipa_routing_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA routing block Failed\n");
+ goto bail;
+ }
+
+ res = configure_ipa_filtering_block();
+ if (res) {
+ TETH_ERR("Configuration of IPA filtering block Failed\n");
+ goto bail;
+ }
+
+ teth_ctx->is_hw_bridge_complete = true;
+ teth_ctx->comp_hw_bridge_in_progress = false;
+bail:
+ mutex_unlock(&f_lock);
+ TETH_DBG_FUNC_EXIT();
+
+ return;
+}
+
+static void mac_addr_to_str(u8 mac_addr[ETH_ALEN],
+ char *buff,
+ uint buff_size)
+{
+ scnprintf(buff, buff_size, "%02x-%02x-%02x-%02x-%02x-%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
+ mac_addr[4], mac_addr[5]);
+}
+
+static void check_to_complete_hw_bridge(struct sk_buff *skb,
+ u8 *my_mac_addr,
+ bool *my_mac_addr_known,
+ bool *peer_mac_addr_known)
+{
+ bool both_mac_addresses_known;
+ char mac_addr_str[20];
+
+ if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) &&
+ (!(*my_mac_addr_known))) {
+ memcpy(my_mac_addr, &skb->data[ETH_ALEN], ETH_ALEN);
+ mac_addr_to_str(my_mac_addr,
+ mac_addr_str,
+ sizeof(mac_addr_str)-1);
+ TETH_DBG("Extracted MAC addr: %s\n", mac_addr_str);
+ *my_mac_addr_known = true;
+ }
+
+ both_mac_addresses_known = *my_mac_addr_known && *peer_mac_addr_known;
+ if ((both_mac_addresses_known ||
+ (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP)) &&
+ (!teth_ctx->comp_hw_bridge_in_progress) &&
+ (teth_ctx->aggr_params_known)) {
+ INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
+ teth_ctx->comp_hw_bridge_in_progress = true;
+ schedule_work(&teth_ctx->comp_hw_bridge_work);
+ }
+}
+
+static void usb_notify_cb(void *priv,
+ enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ int res;
+
+ switch (evt) {
+ case IPA_RECEIVE:
+ if (!teth_ctx->is_hw_bridge_complete)
+ check_to_complete_hw_bridge(
+ skb,
+ teth_ctx->mac_addresses.host_pc_mac_addr,
+ &teth_ctx->mac_addresses.host_pc_mac_addr_known,
+ &teth_ctx->mac_addresses.device_mac_addr_known);
+
+ /* Send the packet to A2, using a2_service driver API */
+ res = a2_mux_write(A2_MUX_TETHERED_0, skb);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ break;
+
+ case IPA_WRITE_DONE:
+ dev_kfree_skb(skb);
+ break;
+
+ default:
+ TETH_ERR("Unsupported IPA event !\n");
+ WARN_ON(1);
+ }
+
+ return;
+}
+
+static void a2_notify_cb(void *user_data,
+ enum a2_mux_event_type event,
+ unsigned long data)
+{
+ struct sk_buff *skb = (struct sk_buff *)data;
+ int res;
+
+ switch (event) {
+ case A2_MUX_RECEIVE:
+ if (!teth_ctx->is_hw_bridge_complete)
+ check_to_complete_hw_bridge(
+ skb,
+ teth_ctx->mac_addresses.device_mac_addr,
+ &teth_ctx->mac_addresses.device_mac_addr_known,
+ &teth_ctx->
+ mac_addresses.host_pc_mac_addr_known);
+
+ /* Send the packet to USB */
+ res = ipa_tx_dp(IPA_CLIENT_USB_CONS, skb, NULL);
+ if (res) {
+ TETH_ERR("Packet send failure, dropping packet !\n");
+ dev_kfree_skb(skb);
+ }
+ break;
+
+ case A2_MUX_WRITE_DONE:
+ dev_kfree_skb(skb);
+ break;
+
+ default:
+ TETH_ERR("Unsupported IPA event !\n");
+ WARN_ON(1);
+ }
+
+ return;
+}
+
+static void bridge_prod_notify_cb(void *notify_cb_data,
+ enum ipa_rm_event event,
+ unsigned long data)
+{
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ complete(&teth_ctx->is_bridge_prod_up);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ complete(&teth_ctx->is_bridge_prod_down);
+ break;
+
+ default:
+ TETH_ERR("Unsupported notification!\n");
+ WARN_ON(1);
+ break;
+ }
+
+ return;
+}
+
+/**
+* teth_bridge_init() - Initialize the Tethering bridge driver
+* @usb_notify_cb_ptr: Callback function which should be used
+* by the caller. Output parameter.
+* @private_data_ptr: Data for the callback function. Should
+* be used by the caller. Output parameter.
+* Return codes: 0: success,
+* -EINVAL - Bad parameter
+* Other negative value - Failure
+*/
+int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr)
+{
+ int res = 0;
+ struct ipa_rm_create_params bridge_prod_params;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (usb_notify_cb_ptr == NULL) {
+ TETH_ERR("Bad parameter\n");
+ res = -EINVAL;
+ goto bail;
+ }
+
+ *usb_notify_cb_ptr = usb_notify_cb;
+ *private_data_ptr = NULL;
+
+ /* Build IPA Resource manager dependency graph */
+ bridge_prod_params.name = IPA_RM_RESOURCE_BRIDGE_PROD;
+ bridge_prod_params.reg_params.user_data = NULL;
+ bridge_prod_params.reg_params.notify_cb = bridge_prod_notify_cb;
+ res = ipa_rm_create_resource(&bridge_prod_params);
+ if (res) {
+ TETH_ERR("ipa_rm_create_resource() failed\n");
+ goto bail;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto bail;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_1;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_2;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ if (res) {
+ TETH_ERR("ipa_rm_add_dependency() failed\n");
+ goto fail_add_dependency_3;
+ }
+
+ init_completion(&teth_ctx->is_bridge_prod_up);
+ init_completion(&teth_ctx->is_bridge_prod_down);
+
+ /* The default link protocol is Ethernet */
+ teth_ctx->link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+ goto bail;
+
+fail_add_dependency_3:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+fail_add_dependency_2:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_A2_CONS);
+fail_add_dependency_1:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+bail:
+ TETH_DBG_FUNC_EXIT();
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_init);
+
+/**
+* teth_bridge_disconnect() - Disconnect tethering bridge module
+*
+* Return codes: 0: success
+* -EPERM: Operation not permitted as the bridge is already
+* disconnected
+*/
+int teth_bridge_disconnect(void)
+{
+ int res = -EPERM;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (!teth_ctx->is_connected) {
+ TETH_ERR(
+ "Trying to disconnect an already disconnected bridge\n");
+ goto bail;
+ }
+
+ teth_ctx->is_connected = false;
+
+ res = ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res == -EINPROGRESS)
+ wait_for_completion(&teth_ctx->is_bridge_prod_down);
+
+bail:
+ TETH_DBG_FUNC_EXIT();
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_disconnect);
+
+/**
+* teth_bridge_connect() - Connect bridge for a tethered Rmnet / MBIM call
+* @connect_params: Connection info
+*
+* Return codes: 0: success
+* -EINVAL: invalid parameters
+* -EPERM: Operation not permitted as the bridge is already
+* connected
+*/
+int teth_bridge_connect(struct teth_bridge_connect_params *connect_params)
+{
+ int res;
+ struct ipa_ep_cfg ipa_ep_cfg;
+
+ TETH_DBG_FUNC_ENTRY();
+ if (teth_ctx->is_connected) {
+ TETH_ERR("Trying to connect an already connected bridge !\n");
+ return -EPERM;
+ }
+ if (connect_params == NULL ||
+ connect_params->ipa_usb_pipe_hdl <= 0 ||
+ connect_params->usb_ipa_pipe_hdl <= 0 ||
+ connect_params->tethering_mode >= TETH_TETHERING_MODE_MAX ||
+ connect_params->tethering_mode < 0)
+ return -EINVAL;
+
+ teth_ctx->ipa_usb_pipe_hdl = connect_params->ipa_usb_pipe_hdl;
+ teth_ctx->usb_ipa_pipe_hdl = connect_params->usb_ipa_pipe_hdl;
+ teth_ctx->tethering_mode = connect_params->tethering_mode;
+
+ res = ipa_rm_request_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ if (res < 0) {
+ if (res == -EINPROGRESS)
+ wait_for_completion(&teth_ctx->is_bridge_prod_up);
+ else
+ goto bail;
+ }
+
+ res = a2_mux_open_channel(A2_MUX_TETHERED_0,
+ NULL,
+ a2_notify_cb);
+ if (res) {
+ TETH_ERR("a2_mux_open_channel() failed\n");
+ goto bail;
+ }
+
+ res = a2_mux_get_tethered_client_handles(A2_MUX_TETHERED_0,
+ &teth_ctx->ipa_a2_pipe_hdl,
+ &teth_ctx->a2_ipa_pipe_hdl);
+ if (res) {
+ TETH_ERR(
+ "a2_mux_get_tethered_client_handles() failed, res = %d\n", res);
+ goto bail;
+ }
+
+ /* Reset the various endpoints configuration */
+ memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
+ ipa_cfg_ep(teth_ctx->ipa_usb_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->usb_ipa_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
+ ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
+
+ teth_ctx->is_connected = true;
+
+ if (teth_ctx->tethering_mode == TETH_TETHERING_MODE_MBIM)
+ teth_ctx->link_protocol = TETH_LINK_PROTOCOL_IP;
+ TETH_DBG_FUNC_EXIT();
+bail:
+ if (res)
+ ipa_rm_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_connect);
+
+static void set_aggr_default_params(struct teth_aggr_params_link *params)
+{
+ if (params->max_datagrams == 0)
+ params->max_datagrams = 16;
+ if (params->max_transfer_size_byte == 0)
+ params->max_transfer_size_byte = 16*1024;
+}
+
+static void teth_set_bridge_mode(enum teth_link_protocol_type link_protocol)
+{
+ teth_ctx->link_protocol = link_protocol;
+ teth_ctx->is_hw_bridge_complete = false;
+ memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
+}
+
+static long teth_bridge_ioctl(struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int res = 0;
+
+ TETH_DBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
+
+ if ((_IOC_TYPE(cmd) != TETH_BRIDGE_IOC_MAGIC) ||
+ (_IOC_NR(cmd) >= TETH_BRIDGE_IOCTL_MAX)) {
+ TETH_ERR("Invalid ioctl\n");
+ return -ENOIOCTLCMD;
+ }
+
+ switch (cmd) {
+ case TETH_BRIDGE_IOC_SET_BRIDGE_MODE:
+ TETH_DBG("TETH_BRIDGE_IOC_SET_BRIDGE_MODE ioctl called\n");
+ if (teth_ctx->link_protocol != arg)
+ teth_set_bridge_mode(arg);
+ break;
+
+ case TETH_BRIDGE_IOC_SET_AGGR_PARAMS:
+ TETH_DBG("TETH_BRIDGE_IOC_SET_AGGR_PARAMS ioctl called\n");
+ res = copy_from_user(&teth_ctx->aggr_params,
+ (struct teth_aggr_params *)arg,
+ sizeof(struct teth_aggr_params));
+ if (res) {
+ TETH_ERR("Error, res = %d\n", res);
+ res = -EFAULT;
+ break;
+ }
+ set_aggr_default_params(&teth_ctx->aggr_params.dl);
+ set_aggr_default_params(&teth_ctx->aggr_params.ul);
+ teth_ctx->aggr_params_known = true;
+ break;
+
+ case TETH_BRIDGE_IOC_GET_AGGR_PARAMS:
+ TETH_DBG("TETH_BRIDGE_IOC_GET_AGGR_PARAMS ioctl called\n");
+ if (copy_to_user((u8 *)arg, (u8 *)&teth_ctx->aggr_params,
+ sizeof(struct teth_aggr_params))) {
+ res = -EFAULT;
+ break;
+ }
+ break;
+
+ case TETH_BRIDGE_IOC_GET_AGGR_CAPABILITIES:
+ {
+ u16 sz;
+ u16 pyld_sz;
+ struct teth_aggr_capabilities caps;
+
+ TETH_DBG("GET_AGGR_CAPABILITIES ioctl called\n");
+ sz = sizeof(struct teth_aggr_capabilities);
+ if (copy_from_user(&caps,
+ (struct teth_aggr_capabilities *)arg,
+ sz)) {
+ res = -EFAULT;
+ break;
+ }
+
+ if (caps.num_protocols < teth_ctx->aggr_caps->num_protocols) {
+ caps.num_protocols = teth_ctx->aggr_caps->num_protocols;
+ if (copy_to_user((struct teth_aggr_capabilities *)arg,
+ &caps,
+ sz)) {
+ res = -EFAULT;
+ break;
+ }
+ TETH_DBG("Not enough space allocated.\n");
+ res = -EAGAIN;
+ break;
+ }
+
+ pyld_sz = sz + caps.num_protocols *
+ sizeof(struct teth_aggr_params_link);
+
+ if (copy_to_user((u8 *)arg,
+ (u8 *)(teth_ctx->aggr_caps),
+ pyld_sz)) {
+ res = -EFAULT;
+ break;
+ }
+ }
+ break;
+ }
+
+ return res;
+}
+
+static void set_aggr_capabilities(void)
+{
+ u16 NUM_PROTOCOLS = 2;
+
+ teth_ctx->aggr_caps = kzalloc(sizeof(struct teth_aggr_capabilities) +
+ NUM_PROTOCOLS *
+ sizeof(struct teth_aggr_params_link),
+ GFP_KERNEL);
+ if (teth_ctx->aggr_caps == NULL) {
+ TETH_ERR("Memory alloc failed for aggregation capabilities.\n");
+ return;
+ }
+
+ teth_ctx->aggr_caps->num_protocols = NUM_PROTOCOLS;
+
+ teth_ctx->aggr_caps->prot_caps[0].aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+ teth_ctx->aggr_caps->prot_caps[0].max_datagrams = 16;
+ teth_ctx->aggr_caps->prot_caps[0].max_transfer_size_byte = 16*1024;
+
+ teth_ctx->aggr_caps->prot_caps[1].aggr_prot = TETH_AGGR_PROTOCOL_TLP;
+ teth_ctx->aggr_caps->prot_caps[1].max_datagrams = 16;
+ teth_ctx->aggr_caps->prot_caps[1].max_transfer_size_byte = 16*1024;
+}
+
+void teth_bridge_get_client_handles(u32 *producer_handle,
+ u32 *consumer_handle)
+{
+ if (producer_handle == NULL || consumer_handle == NULL)
+ return;
+
+ *producer_handle = teth_ctx->usb_ipa_pipe_hdl;
+ *consumer_handle = teth_ctx->ipa_usb_pipe_hdl;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dent;
+static struct dentry *dfile_link_protocol;
+static struct dentry *dfile_get_aggr_params;
+static struct dentry *dfile_set_aggr_protocol;
+
+static ssize_t teth_debugfs_read_link_protocol(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes;
+
+ nbytes = scnprintf(dbg_buff, TETH_MAX_MSG_LEN, "Link protocol = %s\n",
+ (teth_ctx->link_protocol ==
+ TETH_LINK_PROTOCOL_ETHERNET) ?
+ "ETHERNET" :
+ "IP");
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t teth_debugfs_write_link_protocol(struct file *file,
+ const char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ unsigned long missing;
+ enum teth_link_protocol_type link_protocol;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ if (count > 0)
+ dbg_buff[count-1] = '\0';
+
+ if (strcmp(dbg_buff, "ETHERNET") == 0) {
+ link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
+ } else if (strcmp(dbg_buff, "IP") == 0) {
+ link_protocol = TETH_LINK_PROTOCOL_IP;
+ } else {
+ TETH_ERR("Bad link protocol, got %s,\n"
+ "Use <ETHERNET> or <IP>.\n", dbg_buff);
+ return count;
+ }
+
+ teth_set_bridge_mode(link_protocol);
+
+ return count;
+}
+
+static ssize_t teth_debugfs_read_aggr_params(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes = 0;
+ char aggr_str[20];
+
+ aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
+ aggr_str,
+ sizeof(aggr_str)-1);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN,
+ "Aggregation parameters for uplink:\n");
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Aggregation protocol: %s\n",
+ aggr_str);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max transfer size [byte]: %d\n",
+ teth_ctx->aggr_params.ul.max_transfer_size_byte);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max datagrams: %d\n",
+ teth_ctx->aggr_params.ul.max_datagrams);
+
+ aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
+ aggr_str,
+ sizeof(aggr_str)-1);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN,
+ "Aggregation parameters for downlink:\n");
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Aggregation protocol: %s\n",
+ aggr_str);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max transfer size [byte]: %d\n",
+ teth_ctx->aggr_params.dl.max_transfer_size_byte);
+ nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
+ " Max datagrams: %d\n",
+ teth_ctx->aggr_params.dl.max_datagrams);
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t teth_debugfs_set_aggr_protocol(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long missing;
+ enum teth_aggr_protocol_type aggr_prot;
+ int res;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ if (count > 0)
+ dbg_buff[count-1] = '\0';
+
+ set_aggr_default_params(&teth_ctx->aggr_params.dl);
+ set_aggr_default_params(&teth_ctx->aggr_params.ul);
+
+ if (strcmp(dbg_buff, "NONE") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_NONE;
+ } else if (strcmp(dbg_buff, "MBIM") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
+ } else if (strcmp(dbg_buff, "TLP") == 0) {
+ aggr_prot = TETH_AGGR_PROTOCOL_TLP;
+ } else {
+ TETH_ERR("Bad aggregation protocol, got %s,\n"
+ "Use <NONE>, <MBIM> or <TLP>.\n", dbg_buff);
+ return count;
+ }
+
+ teth_ctx->aggr_params.dl.aggr_prot = aggr_prot;
+ teth_ctx->aggr_params.ul.aggr_prot = aggr_prot;
+ teth_ctx->aggr_params_known = true;
+
+ res = teth_set_aggregation();
+ if (res)
+ TETH_ERR("Failed setting aggregation params\n");
+
+ return count;
+}
+
+const struct file_operations teth_link_protocol_ops = {
+ .read = teth_debugfs_read_link_protocol,
+ .write = teth_debugfs_write_link_protocol,
+};
+
+const struct file_operations teth_get_aggr_params_ops = {
+ .read = teth_debugfs_read_aggr_params,
+};
+
+const struct file_operations teth_set_aggr_protocol_ops = {
+ .write = teth_debugfs_set_aggr_protocol,
+};
+
+void teth_debugfs_init(void)
+{
+ const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
+ S_IWUSR | S_IWGRP | S_IWOTH;
+
+ dent = debugfs_create_dir("ipa_teth", 0);
+ if (IS_ERR(dent)) {
+ IPAERR("fail to create folder ipa_teth debug_fs.\n");
+ return;
+ }
+
+ dfile_link_protocol =
+ debugfs_create_file("link_protocol", read_write_mode, dent, 0,
+ &teth_link_protocol_ops);
+ if (!dfile_link_protocol || IS_ERR(dfile_link_protocol)) {
+ IPAERR("fail to create file link_protocol\n");
+ goto fail;
+ }
+
+ dfile_get_aggr_params =
+ debugfs_create_file("get_aggr_params", read_only_mode, dent, 0,
+ &teth_get_aggr_params_ops);
+ if (!dfile_get_aggr_params || IS_ERR(dfile_get_aggr_params)) {
+ IPAERR("fail to create file get_aggr_params\n");
+ goto fail;
+ }
+
+ dfile_set_aggr_protocol =
+ debugfs_create_file("set_aggr_protocol", read_only_mode, dent,
+ 0, &teth_set_aggr_protocol_ops);
+ if (!dfile_set_aggr_protocol || IS_ERR(dfile_set_aggr_protocol)) {
+ IPAERR("fail to create file set_aggr_protocol\n");
+ goto fail;
+ }
+
+ return;
+fail:
+ debugfs_remove_recursive(dent);
+}
+#else
+void teth_debugfs_init(void) {}
+#endif /* CONFIG_DEBUG_FS */
+
+
+static const struct file_operations teth_bridge_drv_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = teth_bridge_ioctl,
+};
+
+/**
+* teth_bridge_driver_init() - Initialize tethering bridge driver
+*
+*/
+int teth_bridge_driver_init(void)
+{
+ int res;
+
+ TETH_DBG("Tethering bridge driver init\n");
+ teth_ctx = kzalloc(sizeof(*teth_ctx), GFP_KERNEL);
+ if (!teth_ctx) {
+ TETH_ERR("kzalloc err.\n");
+ return -ENOMEM;
+ }
+
+ set_aggr_capabilities();
+
+ teth_ctx->class = class_create(THIS_MODULE, TETH_BRIDGE_DRV_NAME);
+
+ res = alloc_chrdev_region(&teth_ctx->dev_num, 0, 1,
+ TETH_BRIDGE_DRV_NAME);
+ if (res) {
+ TETH_ERR("alloc_chrdev_region err.\n");
+ res = -ENODEV;
+ goto fail_alloc_chrdev_region;
+ }
+
+ teth_ctx->dev = device_create(teth_ctx->class, NULL, teth_ctx->dev_num,
+ teth_ctx, TETH_BRIDGE_DRV_NAME);
+ if (IS_ERR(teth_ctx->dev)) {
+ TETH_ERR(":device_create err.\n");
+ res = -ENODEV;
+ goto fail_device_create;
+ }
+
+ cdev_init(&teth_ctx->cdev, &teth_bridge_drv_fops);
+ teth_ctx->cdev.owner = THIS_MODULE;
+ teth_ctx->cdev.ops = &teth_bridge_drv_fops;
+
+ res = cdev_add(&teth_ctx->cdev, teth_ctx->dev_num, 1);
+ if (res) {
+ TETH_ERR(":cdev_add err=%d\n", -res);
+ res = -ENODEV;
+ goto fail_cdev_add;
+ }
+
+ teth_ctx->comp_hw_bridge_in_progress = false;
+
+ teth_debugfs_init();
+ TETH_DBG("Tethering bridge driver init OK\n");
+
+ return 0;
+fail_cdev_add:
+ device_destroy(teth_ctx->class, teth_ctx->dev_num);
+fail_device_create:
+ unregister_chrdev_region(teth_ctx->dev_num, 1);
+fail_alloc_chrdev_region:
+ kfree(teth_ctx->aggr_caps);
+ kfree(teth_ctx);
+ teth_ctx = NULL;
+
+ return res;
+}
+EXPORT_SYMBOL(teth_bridge_driver_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Tethering bridge driver");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index bd1423d..c03ca69 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -95,7 +95,7 @@
static struct regulator *hsusb_3p3;
static struct regulator *hsusb_1p8;
-static struct regulator *hsusb_vddcx;
+static struct regulator *hsusb_vdd;
static struct regulator *vbus_otg;
static struct regulator *mhl_usb_hs_switch;
static struct power_supply *psy;
@@ -111,7 +111,7 @@
#endif
}
-static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+static int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
{ /* VDD_CX CORNER Voting */
[VDD_NONE] = RPM_VREG_CORNER_NONE,
[VDD_MIN] = RPM_VREG_CORNER_NOMINAL,
@@ -175,7 +175,7 @@
int ret;
min_vol = vdd_val[vdd_type][!!high];
- ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
+ ret = regulator_set_voltage(hsusb_vdd, min_vol, max_vol);
if (ret) {
pr_err("%s: unable to set the voltage for regulator "
"HSUSB_VDDCX\n", __func__);
@@ -3802,6 +3802,8 @@
static int __init msm_otg_probe(struct platform_device *pdev)
{
int ret = 0;
+ int len = 0;
+ u32 tmp[3];
struct resource *res;
struct msm_otg *motg;
struct usb_phy *phy;
@@ -3958,24 +3960,40 @@
clk_prepare_enable(motg->pclk);
motg->vdd_type = VDDCX_CORNER;
- hsusb_vddcx = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
- if (IS_ERR(hsusb_vddcx)) {
- hsusb_vddcx = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
- if (IS_ERR(hsusb_vddcx)) {
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
+ if (IS_ERR(hsusb_vdd)) {
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+ if (IS_ERR(hsusb_vdd)) {
dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
- ret = PTR_ERR(hsusb_vddcx);
+ ret = PTR_ERR(hsusb_vdd);
goto devote_xo_handle;
}
motg->vdd_type = VDDCX;
}
+ if (pdev->dev.of_node) {
+ of_get_property(pdev->dev.of_node,
+ "qcom,vdd-voltage-level",
+ &len);
+ if (len == sizeof(tmp)) {
+ of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,vdd-voltage-level",
+ tmp, len/sizeof(*tmp));
+ vdd_val[motg->vdd_type][0] = tmp[0];
+ vdd_val[motg->vdd_type][1] = tmp[1];
+ vdd_val[motg->vdd_type][2] = tmp[2];
+ } else {
+ dev_dbg(&pdev->dev, "Using default hsusb vdd config.\n");
+ }
+ }
+
ret = msm_hsusb_config_vddcx(1);
if (ret) {
dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
goto devote_xo_handle;
}
- ret = regulator_enable(hsusb_vddcx);
+ ret = regulator_enable(hsusb_vdd);
if (ret) {
dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
goto free_config_vddcx;
@@ -3984,7 +4002,7 @@
ret = msm_hsusb_ldo_init(motg, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
- goto free_hsusb_vddcx;
+ goto free_hsusb_vdd;
}
if (pdata->mhl_enable) {
@@ -4167,10 +4185,10 @@
msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
free_ldo_init:
msm_hsusb_ldo_init(motg, 0);
-free_hsusb_vddcx:
- regulator_disable(hsusb_vddcx);
+free_hsusb_vdd:
+ regulator_disable(hsusb_vdd);
free_config_vddcx:
- regulator_set_voltage(hsusb_vddcx,
+ regulator_set_voltage(hsusb_vdd,
vdd_val[motg->vdd_type][VDD_NONE],
vdd_val[motg->vdd_type][VDD_MAX]);
devote_xo_handle:
@@ -4264,8 +4282,8 @@
}
msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
msm_hsusb_ldo_init(motg, 0);
- regulator_disable(hsusb_vddcx);
- regulator_set_voltage(hsusb_vddcx,
+ regulator_disable(hsusb_vdd);
+ regulator_set_voltage(hsusb_vdd,
vdd_val[motg->vdd_type][VDD_NONE],
vdd_val[motg->vdd_type][VDD_MAX]);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 4c70770..e4f78ad 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -323,6 +323,7 @@
struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
struct msm_fb_data_type *mfd);
+int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata);
int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index c640c73..cabb183 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -443,7 +443,7 @@
return NULL;
}
-static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
+int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_ctl *split_ctl;
u32 width, height;
@@ -566,12 +566,6 @@
ctl->opmode |= (ctl->intf_num << 4);
- ret = mdss_mdp_ctl_setup(ctl);
- if (ret) {
- pr_err("unable to setup control path %d\n", ctl->num);
- goto ctl_init_fail;
- }
-
if (ctl->intf_num == MDSS_MDP_NO_INTF) {
ctl->dst_format = pdata->panel_info.out_format;
} else {
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index c1dcc18..daa2499 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -1017,6 +1017,12 @@
return;
}
+ ret = mdss_mdp_overlay_start(mfd);
+ if (ret) {
+ pr_err("unable to start overlay %d (%d)\n", mfd->index, ret);
+ return;
+ }
+
if (is_mdss_iommu_attached())
data.p[0].addr = mfd->iova;
else
@@ -1454,6 +1460,10 @@
rc = mdss_mdp_overlay_start(mfd);
if (!IS_ERR_VALUE(rc))
rc = mdss_mdp_overlay_kickoff(mfd->ctl);
+ } else {
+ rc = mdss_mdp_ctl_setup(mfd->ctl);
+ if (rc)
+ return rc;
}
if (!IS_ERR_VALUE(rc) && mfd->vsync_pending) {
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index d3ee879..7a5ab0d 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -112,10 +112,10 @@
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 24
-#define EVENT_LAST_ID 0x099F
+#define EVENT_LAST_ID 0x09AB
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 93
+#define MSG_SSID_0_LAST 94
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -278,6 +278,9 @@
MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW,
+ MSG_LVL_HIGH,
MSG_LVL_LOW
};
@@ -713,7 +716,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x1750
+#define LOG_1 0x1755
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h
index 285b593..8a1b3a1 100644
--- a/include/linux/dma-contiguous.h
+++ b/include/linux/dma-contiguous.h
@@ -70,7 +70,7 @@
void dma_contiguous_reserve(phys_addr_t addr_limit);
int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base,
- phys_addr_t limit);
+ phys_addr_t limit, const char *name);
int dma_contiguous_add_device(struct device *dev, phys_addr_t base);
@@ -91,7 +91,7 @@
phys_addr_t base, phys_addr_t limit)
{
int ret;
- ret = dma_contiguous_reserve_area(size, &base, limit);
+ ret = dma_contiguous_reserve_area(size, &base, limit, NULL);
if (ret == 0)
ret = dma_contiguous_add_device(dev, base);
return ret;
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index c6f0730..30bf4f2 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -769,4 +769,87 @@
IPA_IOCTL_PULL_MSG, \
struct ipa_msg_meta *)
+/*
+ * unique magic number of the Tethering bridge ioctls
+ */
+#define TETH_BRIDGE_IOC_MAGIC 0xCE
+
+/*
+ * Ioctls supported by Tethering bridge driver
+ */
+#define TETH_BRIDGE_IOCTL_SET_BRIDGE_MODE 0
+#define TETH_BRIDGE_IOCTL_SET_AGGR_PARAMS 1
+#define TETH_BRIDGE_IOCTL_GET_AGGR_PARAMS 2
+#define TETH_BRIDGE_IOCTL_GET_AGGR_CAPABILITIES 3
+#define TETH_BRIDGE_IOCTL_MAX 4
+
+
+/**
+ * enum teth_link_protocol_type - link protocol (IP / Ethernet)
+ */
+enum teth_link_protocol_type {
+ TETH_LINK_PROTOCOL_IP,
+ TETH_LINK_PROTOCOL_ETHERNET,
+ TETH_LINK_PROTOCOL_MAX,
+};
+
+/**
+ * enum teth_aggr_protocol_type - Aggregation protocol (MBIM / TLP)
+ */
+enum teth_aggr_protocol_type {
+ TETH_AGGR_PROTOCOL_NONE,
+ TETH_AGGR_PROTOCOL_MBIM,
+ TETH_AGGR_PROTOCOL_TLP,
+ TETH_AGGR_PROTOCOL_MAX,
+};
+
+/**
+ * struct teth_aggr_params_link - Aggregation parameters for uplink/downlink
+ * @aggr_prot: Aggregation protocol (MBIM / TLP)
+ * @max_transfer_size_byte: Maximal size of aggregated packet in bytes.
+ * Default value is 16*1024.
+ * @max_datagrams: Maximal number of IP packets in an aggregated
+ * packet. Default value is 16
+ */
+struct teth_aggr_params_link {
+ enum teth_aggr_protocol_type aggr_prot;
+ uint32_t max_transfer_size_byte;
+ uint32_t max_datagrams;
+};
+
+
+/**
+ * struct teth_aggr_params - Aggregation parmeters
+ * @ul: Uplink parameters
+ * @dl: Downlink parmaeters
+ */
+struct teth_aggr_params {
+ struct teth_aggr_params_link ul;
+ struct teth_aggr_params_link dl;
+};
+
+/**
+ * struct teth_aggr_capabilities - Aggregation capabilities
+ * @num_protocols: Number of protocols described in the array
+ * @prot_caps[]: Array of aggregation capabilities per protocol
+ */
+struct teth_aggr_capabilities {
+ uint16_t num_protocols;
+ struct teth_aggr_params_link prot_caps[0];
+};
+
+
+#define TETH_BRIDGE_IOC_SET_BRIDGE_MODE _IOW(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_SET_BRIDGE_MODE, \
+ enum teth_link_protocol_type)
+#define TETH_BRIDGE_IOC_SET_AGGR_PARAMS _IOW(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_SET_AGGR_PARAMS, \
+ struct teth_aggr_params *)
+#define TETH_BRIDGE_IOC_GET_AGGR_PARAMS _IOR(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_GET_AGGR_PARAMS, \
+ struct teth_aggr_params *)
+#define TETH_BRIDGE_IOC_GET_AGGR_CAPABILITIES _IOWR(TETH_BRIDGE_IOC_MAGIC, \
+ TETH_BRIDGE_IOCTL_GET_AGGR_CAPABILITIES, \
+ struct teth_aggr_capabilities *)
+
#endif /* _MSM_IPA_H_ */
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index c6ee4f0..0683296 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -365,8 +365,8 @@
* requests to connect to a specified network but without separating
* auth and assoc steps. For this, you need to specify the SSID in a
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
- * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
+ * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
* Background scan period can optionally be
@@ -906,7 +906,7 @@
* @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
* used for the association (&enum nl80211_mfp, represented as a u32);
* this attribute can be used
- * with %NL80211_CMD_ASSOCIATE request
+ * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
*
* @NL80211_ATTR_STA_FLAGS2: Attribute containing a
* &struct nl80211_sta_flag_update.
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5e32ff7..6666c69 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1199,6 +1199,7 @@
* @ie: IEs for association request
* @ie_len: Length of assoc_ie in octets
* @privacy: indicates whether privacy-enabled APs should be used
+ * @mfp: indicate whether management frame protection is used
* @crypto: crypto settings
* @key_len: length of WEP key for shared key authentication
* @key_idx: index of WEP key for shared key authentication
@@ -1219,6 +1220,7 @@
u8 *ie;
size_t ie_len;
bool privacy;
+ enum nl80211_mfp mfp;
struct cfg80211_crypto_settings crypto;
const u8 *key;
u8 key_len, key_idx;
diff --git a/kernel/sys.c b/kernel/sys.c
index e7006eb..39791be 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1179,15 +1179,16 @@
* Work around broken programs that cannot handle "Linux 3.0".
* Instead we map 3.x to 2.6.40+x, so e.g. 3.0 would be 2.6.40
*/
-static int override_release(char __user *release, int len)
+static int override_release(char __user *release, size_t len)
{
int ret = 0;
- char buf[65];
if (current->personality & UNAME26) {
- char *rest = UTS_RELEASE;
+ const char *rest = UTS_RELEASE;
+ char buf[65] = { 0 };
int ndots = 0;
unsigned v;
+ size_t copy;
while (*rest) {
if (*rest == '.' && ++ndots >= 3)
@@ -1197,8 +1198,9 @@
rest++;
}
v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
- snprintf(buf, len, "2.6.%u%s", v, rest);
- ret = copy_to_user(release, buf, len);
+ copy = min(sizeof(buf), max_t(size_t, 1, len));
+ copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
+ ret = copy_to_user(release, buf, copy + 1);
}
return ret;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5c2e805..1ccc69e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -5173,6 +5173,15 @@
connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
+ if (info->attrs[NL80211_ATTR_USE_MFP]) {
+ connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+ if (connect.mfp != NL80211_MFP_REQUIRED &&
+ connect.mfp != NL80211_MFP_NO)
+ return -EINVAL;
+ } else {
+ connect.mfp = NL80211_MFP_NO;
+ }
+
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
connect.channel =
ieee80211_get_channel(wiphy,
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index bbbed73..ab91446 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -190,7 +190,8 @@
prev_bssid,
params->ssid, params->ssid_len,
params->ie, params->ie_len,
- false, ¶ms->crypto,
+ params->mfp != NL80211_MFP_NO,
+ ¶ms->crypto,
params->flags, ¶ms->ht_capa,
¶ms->ht_capa_mask);
if (err)
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 7f17eef..b3d4901 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -932,12 +932,6 @@
SOC_SINGLE_TLV("ADC5 Volume", TAIKO_A_TX_5_6_EN, 5, 3, 0, analog_gain),
SOC_SINGLE_TLV("ADC6 Volume", TAIKO_A_TX_5_6_EN, 1, 3, 0, analog_gain),
-
- SOC_SINGLE("MICBIAS1 CAPLESS Switch", TAIKO_A_MICB_1_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS2 CAPLESS Switch", TAIKO_A_MICB_2_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS3 CAPLESS Switch", TAIKO_A_MICB_3_CTL, 4, 1, 1),
- SOC_SINGLE("MICBIAS4 CAPLESS Switch", TAIKO_A_MICB_4_CTL, 4, 1, 1),
-
SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 0, 100, taiko_get_anc_slot,
taiko_put_anc_slot),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 1969fe8..c14cb74 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -503,6 +503,7 @@
rc = -EINVAL;
}
+ kfree(voice_ocm_work);
return;
}
/**
@@ -614,6 +615,7 @@
rc = -EINVAL;
}
+ kfree(audio_ocm_work);
return;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 2d2fe31..d0b5500 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -148,7 +148,6 @@
*/
snd_pcm_stream_lock_irq(substream);
if (snd_pcm_playback_empty(substream)) {
- atomic_set(&prtd->pending_buffer, 1);
runtime->render_flag |= SNDRV_RENDER_STOPPED;
stop_playback = 1;
}
@@ -1038,7 +1037,6 @@
(prtd->out_head + 1) & (runtime->periods - 1);
runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
- atomic_set(&prtd->pending_buffer, 0);
return 0;
}
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index ae7e76c..3a4a674 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -536,8 +536,8 @@
memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
rc = q6asm_get_session_time(prtd->audio_client, ×tamp);
if (rc < 0) {
- pr_err("%s: Get Session Time return value =%lld\n",
- __func__, timestamp);
+ pr_err("%s: Fail to get session time stamp, rc:%d\n",
+ __func__, rc);
return -EAGAIN;
}
temp = (timestamp * 2 * runtime->channels);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 913dded..1f2f307 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -223,6 +223,12 @@
adm_params->hdr.dest_svc = APR_SVC_ADM;
adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
index = afe_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d portid %#x\n",
+ __func__, index, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
adm_params->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
adm_params->hdr.token = port_id;
adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;