summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-ba.txt41
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-ba.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi67
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi21
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts3
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts5
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi4
-rw-r--r--arch/arm64/configs/sdm660_defconfig1
-rw-r--r--drivers/crypto/msm/qcrypto.c29
-rw-r--r--drivers/gpu/drm/msm/msm_smmu.c28
-rw-r--r--drivers/gpu/drm/msm/sde/sde_plane.c107
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c12
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c5
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c22
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c58
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/core/sd.c5
-rw-r--r--drivers/mmc/host/sdhci-msm.c2
-rw-r--r--drivers/mmc/host/sdhci.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_utils.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c5
-rw-r--r--drivers/scsi/ufs/ufshcd.c11
-rw-r--r--drivers/soc/qcom/icnss.c293
-rwxr-xr-xdrivers/soundwire/soundwire.c71
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c14
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c4
-rw-r--r--drivers/video/msm/ba/Kconfig12
-rw-r--r--drivers/video/msm/ba/Makefile5
-rw-r--r--drivers/video/msm/ba/msm_ba.c892
-rw-r--r--drivers/video/msm/ba/msm_ba_common.c645
-rw-r--r--drivers/video/msm/ba/msm_ba_common.h40
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.c223
-rw-r--r--drivers/video/msm/ba/msm_ba_debug.h84
-rw-r--r--drivers/video/msm/ba/msm_ba_internal.h220
-rw-r--r--drivers/video/msm/ba/msm_v4l2_ba.c615
-rw-r--r--include/linux/mmc/host.h2
-rwxr-xr-xinclude/linux/soundwire/soundwire.h6
-rw-r--r--include/media/msm_ba.h81
-rw-r--r--include/soc/qcom/icnss.h7
-rw-r--r--include/uapi/linux/v4l2-controls.h6
-rw-r--r--include/uapi/linux/videodev2.h32
-rw-r--r--net/core/dev.c37
-rw-r--r--sound/soc/codecs/wsa881x.c85
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c1
51 files changed, 3516 insertions, 339 deletions
diff --git a/Documentation/devicetree/bindings/media/video/msm-ba.txt b/Documentation/devicetree/bindings/media/video/msm-ba.txt
new file mode 100644
index 000000000000..9a6fe4d7e8ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ba.txt
@@ -0,0 +1,41 @@
+* Qualcomm Technologies Inc MSM BA
+
+[Root level node]
+==================
+Required properties:
+- compatible: Must be "qcom,msm-ba".
+
+[Subnode]
+==========
+- qcom,ba-input-profile-#: Defines child nodes for the profiles supported
+ by BA driver. Each profile should have properties "qcom,type",
+ "qcom,name", "qcom,ba-input", "qcom,ba-output", "qcom,sd-name",
+ "qcom,ba-node" and "qcom,user-type".
+Required properties:
+- qcom,type: Input type such as CVBS(0), HDMI(4) etc as defined in BA driver.
+ This property is of type u32.
+- qcom,name: Name of the input type. This property is of type string.
+- qcom,ba-input: BA input id supported by a bridge chip for this profile.
+ This property is of type u32.
+- qcom,ba-output: BA output id for the profile. This property is of type u32.
+- qcom,sd-name: Name of the sub-device driver associated with this profile.
+ This property is of type string.
+- qcom,ba-node: Defines the ba node id. This is the avdevice node used by camera
+ for this profile. This property is of type u32.
+- qcom,user-type: This property defines how the profile is being used. If this
+ profile is used by kernel it is set to 0 and if used by userspace
+ it is set to 1. This property is of type u32.
+Example:
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
diff --git a/arch/arm/boot/dts/qcom/apq8096-ba.dtsi b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
new file mode 100644
index 000000000000..e6524593e502
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apq8096-ba.dtsi
@@ -0,0 +1,18 @@
+/* Copyright (c) 2015, 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.
+ */
+
+&soc {
+ msm_ba: qcom,ba {
+ compatible = "qcom,msm-ba";
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
index bfc6f210a0bb..e731c7edd518 100644
--- a/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-dragonboard.dtsi
@@ -12,6 +12,7 @@
#include "msm8996-pinctrl.dtsi"
#include "apq8096-camera-sensor-dragonboard.dtsi"
+#include "apq8096-ba.dtsi"
/ {
bluetooth: bt_qca6174 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index a600008341c2..3c9b8e445ff3 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -1098,17 +1098,58 @@
pinctrl-0 = <&quat_tdm_dout_active>;
pinctrl-1 = <&quat_tdm_dout_sleep>;
};
+
+ qcom,adv7481@70 {
+ compatible = "qcom,adv7481";
+ reg = <0x70 0xff>;
+ cam_vdig-supply = <&pm8994_s3>;
+ /* Cameras powered by PMIC: */
+ cam_vio-supply = <&pm8994_lvs1>;
+ cam_vana-supply = <&pm8994_l17>;
+ /* Self-powered cameras: */
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 2500000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 17 0>, /* I2C SDA */
+ <&tlmm 18 0>, /* I2C SCL */
+ <&pm8994_gpios 4 0>, /* RST */
+ <&pm8994_gpios 5 0>, /* INT1 */
+ <&pm8994_gpios 6 0>, /* INT2 */
+ <&pm8994_gpios 7 0>; /* INT3 */
+ };
+
+ qcom,msm-ba {
+ compatible = "qcom,msm-ba";
+ qcom,ba-input-profile-0 {
+ qcom,type = <4>; /* input type */
+ qcom,name = "HDMI-1"; /* input name */
+ qcom,ba-input = <13>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <0>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+
+ qcom,ba-input-profile-1 {
+ qcom,type = <0>; /* input type */
+ qcom,name = "CVBS-0"; /* input name */
+ qcom,ba-input = <0>; /* ba input id */
+ qcom,ba-output = <0>; /* ba output id */
+ qcom,sd-name = "adv7481"; /* sd name */
+ qcom,ba-node = <1>; /* ba node */
+ qcom,user-type = <1>; /* user type */
+ };
+ };
};
&pm8994_gpios {
- gpio@c600 { /* GPIO 7 - NFC DWL REQ */
- qcom,mode = <1>;
- qcom,output-type = <0>;
- qcom,pull = <5>;
+ gpio@c600 { /* GPIO 7 - adv7481 INT3 */
+ qcom,mode = <0>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
qcom,src-sel = <0>;
- qcom,master-en = <1>;
status = "okay";
};
@@ -1159,17 +1200,23 @@
status = "okay";
};
- gpio@c300 { /* GPIO 4 */
- qcom,mode = <0>;
+ gpio@c300 { /* GPIO 4 - adv7481 RST */
+ qcom,mode = <1>;
qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
};
- gpio@c400 { /* GPIO 5 */
+ gpio@c400 { /* GPIO 5 - adv7481 INT1 */
+ qcom,mode = <0>;
+ qcom,vin-sel = <2>;
+ qcom,src-sel = <0>;
+ status = "okay";
+ };
+
+ gpio@c500 { /* GPIO 6 - adv7481 INT2*/
qcom,mode = <0>;
- qcom,pull = <0>;
qcom,vin-sel = <2>;
qcom,src-sel = <0>;
status = "okay";
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 7c07102a1fed..797b8a8d8e82 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -868,11 +868,12 @@
asoc-codec-names = "msm-stub-codec.1";
};
- usb_detect {
+ usb_detect: usb_detect {
compatible = "qcom,gpio-usbdetect";
+ qcom,vbus-det-gpio = <&pm8994_gpios 17 0>;
interrupt-parent = <&spmi_bus>;
- interrupts = <0x0 0xd0 0x0>; /* PM8994 GPIO17 */
- interrupt-names = "vbus_det_irq";
+ interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>;
+ interrupt-names ="pmic_id_irq";
};
loopback1: qcom,msm-pcm-loopback-low-latency {
@@ -1071,18 +1072,10 @@
};
&usb3 {
- interrupt-parent = <&usb3>;
- interrupts = <0 1 2 3>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0x0 0xffffffff>;
- interrupt-map = <0x0 0 &intc 0 0 347 0
- 0x0 1 &intc 0 0 243 0
- 0x0 2 &intc 0 0 180 0
- 0x0 3 &spmi_bus 0x0 0x0 0x9 0x0>;
- interrupt-names = "hs_phy_irq", "ss_phy_irq", "pwr_event_irq",
- "pmic_id_irq";
-
+ extcon = <&usb_detect>;
vbus_dwc3-supply = <&usb_otg_switch>;
+ vdda33-supply = <&pm8994_l24>;
+ vdda18-supply = <&pm8994_l12>;
};
&blsp1_uart2 {
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
index 668cb2844363..1cf61486c9e8 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts
@@ -42,9 +42,6 @@
i2c@75b6000 { /* BLSP8 */
/* ADV7533 HDMI Bridge Chip removed on ADP Lite */
- adv7533@3d {
- status = "disabled";
- };
adv7533@39 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
index 1ab8ee9cd538..7f6f3d5d4a4c 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
+++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts
@@ -46,6 +46,11 @@
qcom,hotplug-temp-hysteresis = <25>;
qcom,therm-reset-temp = <119>;
};
+
+ qcom,adv7481@70 {
+ qcom,cam-vreg-min-voltage = <1300000 0 1800000>;
+ qcom,cam-vreg-max-voltage = <1300000 0 1800000>;
+ };
};
&pil_modem {
diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
index ca89a517df5c..252940c9c3e5 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
@@ -1331,6 +1331,10 @@
qcom,poll-ms = <50>;
qcom,limit-temp = <80>;
qcom,core-limit-temp = <90>;
+ msm_thermal_freq: qcom,vdd-apps-rstr {
+ qcom,max-freq-level = <1209600>;
+ qcom,levels = <1056000 1516800 1516800>;
+ };
qcom,vdd-gfx-rstr{
qcom,levels = <6 8 9>; /* Nominal, Turbo, Turbo_L1 */
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index f75794ba942f..99766dbcdfe5 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -268,14 +268,18 @@
compatible = "qcom,qusb2phy";
reg = <0x0c012000 0x180>,
<0x01fcb24c 0x4>,
+ <0x00780240 0x4>,
<0x00188018 0x4>;
reg-names = "qusb_phy_base",
"tcsr_clamp_dig_n_1p8",
+ "tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm660l_l1>;
vdda18-supply = <&pm660_l10>;
vdda33-supply = <&pm660l_l7>;
qcom,vdd-voltage-level = <0 925000 925000>;
+ qcom,tune2-efuse-bit-pos = <25>;
+ qcom,tune2-efuse-num-bits = <4>;
qcom,qusb-phy-init-seq = <0xf8 0x80
0xb3 0x84
0x83 0x88
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index aafde733099b..5084a5505111 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -8,6 +8,7 @@ CONFIG_TASKSTATS=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 893b0b6da6b8..b5c5dc035c66 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -437,6 +437,7 @@ struct qcrypto_cipher_req_ctx {
u8 rfc4309_iv[QCRYPTO_MAX_IV_LENGTH];
unsigned int ivsize;
int aead;
+ int ccmtype; /* default: 0, rfc4309: 1 */
struct scatterlist asg; /* Formatted associated data sg */
unsigned char *adata; /* Pointer to formatted assoc data */
enum qce_cipher_alg_enum alg;
@@ -1897,9 +1898,8 @@ static int aead_ccm_set_msg_len(u8 *block, unsigned int msglen, int csize)
return 0;
}
-static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
+static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq, uint32_t assoclen)
{
- struct aead_request *areq = (struct aead_request *) qreq->areq;
unsigned int i = ((unsigned int)qreq->iv[0]) + 1;
memcpy(&qreq->nonce[0] , qreq->iv, qreq->ivsize);
@@ -1908,7 +1908,7 @@ static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq)
* NIST Special Publication 800-38C
*/
qreq->nonce[0] |= (8 * ((qreq->authsize - 2) / 2));
- if (areq->assoclen)
+ if (assoclen)
qreq->nonce[0] |= 64;
if (i > MAX_NONCE)
@@ -2118,24 +2118,31 @@ static int _qcrypto_process_aead(struct crypto_engine *pengine,
qreq.flags = cipher_ctx->flags;
if (qreq.mode == QCE_MODE_CCM) {
+ uint32_t assoclen;
+
if (qreq.dir == QCE_ENCRYPT)
qreq.cryptlen = req->cryptlen;
else
qreq.cryptlen = req->cryptlen -
qreq.authsize;
+
+ /* if rfc4309 ccm, adjust assoclen */
+ assoclen = req->assoclen;
+ if (rctx->ccmtype)
+ assoclen -= 8;
/* Get NONCE */
- ret = qccrypto_set_aead_ccm_nonce(&qreq);
+ ret = qccrypto_set_aead_ccm_nonce(&qreq, assoclen);
if (ret)
return ret;
- if (req->assoclen) {
- rctx->adata = kzalloc((req->assoclen + 0x64),
+ if (assoclen) {
+ rctx->adata = kzalloc((assoclen + 0x64),
GFP_ATOMIC);
if (!rctx->adata)
return -ENOMEM;
/* Format Associated data */
ret = qcrypto_aead_ccm_format_adata(&qreq,
- req->assoclen,
+ assoclen,
req->src,
rctx->adata);
} else {
@@ -2592,6 +2599,7 @@ static int _qcrypto_aead_encrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_ENCRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2606,6 +2614,8 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2615,6 +2625,7 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_enc++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2922,6 +2933,7 @@ static int _qcrypto_aead_decrypt_aes_ccm(struct aead_request *req)
rctx->dir = QCE_DECRYPT;
rctx->mode = QCE_MODE_CCM;
rctx->iv = req->iv;
+ rctx->ccmtype = 0;
pstat->aead_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
@@ -2935,6 +2947,8 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
struct crypto_stat *pstat;
pstat = &_qcrypto_stat;
+ if (req->assoclen != 16 && req->assoclen != 20)
+ return -EINVAL;
rctx = aead_request_ctx(req);
rctx->aead = 1;
rctx->alg = CIPHER_ALG_AES;
@@ -2944,6 +2958,7 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req)
rctx->rfc4309_iv[0] = 3; /* L -1 */
memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3);
memcpy(&rctx->rfc4309_iv[4], req->iv, 8);
+ rctx->ccmtype = 1;
rctx->iv = rctx->rfc4309_iv;
pstat->aead_rfc4309_ccm_aes_dec++;
return _qcrypto_queue_req(cp, ctx->pengine, &req->base);
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index 7d0dda032c59..7673f54b7691 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -120,16 +120,30 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova,
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
+ struct iommu_domain *domain;
int ret;
- if (priv)
- ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL, priv);
- else
- ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL);
+ if (!client || !sgt)
+ return -EINVAL;
- return (ret != sgt->nents) ? -ENOMEM : 0;
+ if (iova != 0) {
+ if (!client->mmu_mapping || !client->mmu_mapping->domain)
+ return -EINVAL;
+
+ domain = client->mmu_mapping->domain;
+
+ return iommu_map_sg(domain, iova, sgt->sgl,
+ sgt->nents, flags);
+ } else {
+ if (priv)
+ ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl,
+ sgt->nents, DMA_BIDIRECTIONAL, priv);
+ else
+ ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents,
+ DMA_BIDIRECTIONAL);
+
+ return (ret != sgt->nents) ? -ENOMEM : 0;
+ }
}
static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3ccad0c0c08d..85ebff08761b 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -147,6 +147,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state)
return state && state->fb && state->crtc;
}
+static struct sde_kms *_sde_plane_get_kms(struct drm_plane *plane)
+{
+ struct msm_drm_private *priv;
+
+ if (!plane || !plane->dev)
+ return NULL;
+
+ priv = plane->dev->dev_private;
+ if (!priv)
+ return NULL;
+
+ return to_sde_kms(priv->kms);
+}
+
/**
* _sde_plane_calc_fill_level - calculate fill level of the given source format
* @plane: Pointer to drm plane
@@ -582,12 +596,62 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
return ret;
}
+/**
+ * _sde_plane_get_aspace: gets the address space based on the
+ * fb_translation mode property
+ */
+static int _sde_plane_get_aspace(
+ struct sde_plane *psde,
+ struct sde_plane_state *pstate,
+ struct msm_gem_address_space **aspace)
+{
+ struct sde_kms *kms;
+ int mode;
+
+ if (!psde || !pstate || !aspace) {
+ SDE_ERROR("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ kms = _sde_plane_get_kms(&psde->base);
+ if (!kms) {
+ SDE_ERROR("invalid kms\n");
+ return -EINVAL;
+ }
+
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+
+ switch (mode) {
+ case SDE_DRM_FB_NON_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC:
+ *aspace = kms->aspace[MSM_SMMU_DOMAIN_SECURE];
+ if (!aspace)
+ return -EINVAL;
+ break;
+ case SDE_DRM_FB_SEC_DIR_TRANS:
+ case SDE_DRM_FB_NON_SEC_DIR_TRANS:
+ *aspace = NULL;
+ break;
+ default:
+ SDE_ERROR("invalid fb_translation mode:%d\n", mode);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp,
struct sde_plane_state *pstate,
struct sde_hw_pipe_cfg *pipe_cfg,
struct drm_framebuffer *fb)
{
struct sde_plane *psde;
+ struct msm_gem_address_space *aspace = NULL;
int ret;
if (!pp || !pstate || !pipe_cfg || !fb) {
@@ -603,7 +667,13 @@ static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp,
return;
}
- ret = sde_format_populate_layout(psde->aspace, fb, &pipe_cfg->layout);
+ ret = _sde_plane_get_aspace(psde, pstate, &aspace);
+ if (ret) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", ret);
+ return;
+ }
+
+ ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout);
if (ret == -EAGAIN)
SDE_DEBUG_PLANE(psde, "not updating same src addrs\n");
else if (ret)
@@ -1132,9 +1202,9 @@ static int _sde_plane_color_fill(struct sde_phy_plane *pp,
}
static int _sde_plane_mode_set(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *state)
{
- uint32_t nplanes, src_flags;
+ uint32_t nplanes, src_flags = 0x0;
struct sde_plane *psde;
struct sde_plane_state *pstate;
const struct sde_format *fmt;
@@ -1145,6 +1215,7 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
int idx;
struct sde_phy_plane *pp;
uint32_t num_of_phy_planes = 0, maxlinewidth = 0xFFFF;
+ int mode = 0;
if (!plane) {
SDE_ERROR("invalid plane\n");
@@ -1220,6 +1291,15 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
return 0;
memset(&src, 0, sizeof(struct sde_rect));
+
+ /* update secure session flag */
+ mode = sde_plane_get_property(pstate,
+ PLANE_PROP_FB_TRANSLATION_MODE);
+ if ((mode == SDE_DRM_FB_SEC) ||
+ (mode == SDE_DRM_FB_SEC_DIR_TRANS))
+ src_flags |= SDE_SSPP_SECURE_OVERLAY_SESSION;
+
+
/* update roi config */
if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) {
POPULATE_RECT(&src, state->src_x, state->src_y,
@@ -1286,10 +1366,11 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
pp->scaler3_cfg);
}
- if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
+ if (((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) ||
+ (src_flags &
+ SDE_SSPP_SECURE_OVERLAY_SESSION)) &&
pp->pipe_hw->ops.setup_format) {
- src_flags = 0x0;
- SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n",
+ SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n",
sde_plane_get_property(pstate, PLANE_PROP_ROTATION));
if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION)
& BIT(DRM_REFLECT_X))
@@ -1344,10 +1425,23 @@ static int sde_plane_prepare_fb(struct drm_plane *plane,
{
struct drm_framebuffer *fb = new_state->fb;
struct sde_plane *psde = to_sde_plane(plane);
+ struct sde_plane_state *pstate;
+ int rc;
+
+ if (!psde || !new_state)
+ return -EINVAL;
if (!new_state->fb)
return 0;
+ pstate = to_sde_plane_state(new_state);
+ rc = _sde_plane_get_aspace(psde, pstate, &psde->aspace);
+
+ if (rc) {
+ SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", rc);
+ return rc;
+ }
+
SDE_DEBUG_PLANE(psde, "FB[%u]\n", fb->base.id);
return msm_framebuffer_prepare(fb, psde->aspace);
}
@@ -2631,7 +2725,6 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
/* cache local stuff for later */
plane = &psde->base;
- psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE];
INIT_LIST_HEAD(&psde->phy_plane_head);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index 03d1b3c22d61..6515e3d6ecbc 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -1360,6 +1360,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
{
uint16_t first_pixel, last_pixel, first_line, last_line;
struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ struct msm_vfe_testgen_cfg *testgen_cfg = &pix_cfg->testgen_cfg;
uint32_t val, subsample_period, subsample_pattern;
uint32_t irq_sub_period = 32;
uint32_t frame_sub_period = 32;
@@ -1383,8 +1384,15 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
subsample_period = camif_cfg->subsample_cfg.irq_subsample_period;
subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern;
- msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
- (camif_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0x484);
+ if (pix_cfg->input_mux == TESTGEN)
+ msm_camera_io_w((testgen_cfg->lines_per_frame - 1) << 16 |
+ (testgen_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+ else
+ msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 |
+ (camif_cfg->pixels_per_line - 1),
+ vfe_dev->vfe_base + 0x484);
+
if (bus_sub_en) {
val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
val &= 0xFFFFFFDF;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index ee695bf5dfd9..648249916be4 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -1263,7 +1263,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
&update_cmd->update_info[i];
/*check array reference bounds*/
if (STATS_IDX(update_info->stream_handle)
- > vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s: stats idx %d out of bound!", __func__,
STATS_IDX(update_info->stream_handle));
return -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index f19e6dd1cb01..d30d8022b7ab 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -479,8 +479,12 @@ static int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
if (input_cfg->d.pix_cfg.input_mux == CAMIF ||
input_cfg->d.pix_cfg.input_mux == TESTGEN) {
- vfe_dev->axi_data.src_info[VFE_PIX_0].width =
- input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ if (input_cfg->d.pix_cfg.input_mux == TESTGEN)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.testgen_cfg.pixels_per_line;
if (input_cfg->d.pix_cfg.camif_cfg.subsample_cfg.
sof_counter_step > 0) {
vfe_dev->axi_data.src_info[VFE_PIX_0].
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index ab7d4e86dcac..ab981f762dd2 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1021,6 +1021,11 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif,
for (i = 0; i < params->num; i++) {
vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type %d\n", __func__,
+ vfe_intf);
+ return;
+ }
if (params->entries[i].intftype == PIX0 &&
params->stereo_enable &&
params->right_entries[i].csid < CSID_MAX &&
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 3b5fbea512ba..0764a18a7993 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -2235,6 +2235,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct hal_enable_picture enable_picture;
struct hal_enable hal_property;
enum hal_property property_id = 0;
+ enum hal_video_codec codec;
u32 property_val = 0;
void *pdata = NULL;
struct hfi_device *hdev;
@@ -2289,12 +2290,23 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE:
property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE;
if (ctrl->val ==
- V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON)
+ V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) {
enable_picture.picture_type = HAL_PICTURE_I;
- else
- enable_picture.picture_type = HAL_PICTURE_I |
- HAL_PICTURE_P | HAL_PICTURE_B |
- HAL_PICTURE_IDR;
+ } else {
+ codec = get_hal_codec(inst->fmts[OUTPUT_PORT].fourcc);
+ if (codec == HAL_VIDEO_CODEC_H264) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR;
+ } else if (codec == HAL_VIDEO_CODEC_HEVC) {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B |
+ HAL_PICTURE_IDR | HAL_PICTURE_CRA;
+ } else {
+ enable_picture.picture_type = HAL_PICTURE_I |
+ HAL_PICTURE_P | HAL_PICTURE_B;
+ }
+ }
pdata = &enable_picture;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO:
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 5419bd1655c1..b292ea70fb40 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -26,11 +26,14 @@
#include <linux/debugfs.h>
#include <linux/msm_audio_ion.h>
#include <linux/compat.h>
+#include <linux/mutex.h>
#include "audio_utils_aio.h"
#ifdef CONFIG_USE_DEV_CTRL_VOLUME
#include <linux/qdsp6v2/audio_dev_ctl.h>
#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+static DEFINE_MUTEX(lock);
#ifdef CONFIG_DEBUG_FS
+
int audio_aio_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -43,29 +46,37 @@ ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
const int debug_bufmax = 4096;
static char buffer[4096];
int n = 0;
- struct q6audio_aio *audio = file->private_data;
+ struct q6audio_aio *audio;
- mutex_lock(&audio->lock);
- n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "enabled %d\n", audio->enabled);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "stopped %d\n", audio->stopped);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "feedback %d\n", audio->feedback);
- mutex_unlock(&audio->lock);
- /* Following variables are only useful for debugging when
- * when playback halts unexpectedly. Thus, no mutual exclusion
- * enforced
- */
- n += scnprintf(buffer + n, debug_bufmax - n,
- "wflush %d\n", audio->wflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "rflush %d\n", audio->rflush);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "inqueue empty %d\n", list_empty(&audio->in_queue));
- n += scnprintf(buffer + n, debug_bufmax - n,
- "outqueue empty %d\n", list_empty(&audio->out_queue));
+ mutex_lock(&lock);
+ if (file->private_data != NULL) {
+ audio = file->private_data;
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n",
+ audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "feedback %d\n", audio->feedback);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "inqueue empty %d\n",
+ list_empty(&audio->in_queue));
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "outqueue empty %d\n",
+ list_empty(&audio->out_queue));
+ }
+ mutex_unlock(&lock);
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
@@ -573,6 +584,7 @@ int audio_aio_release(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = file->private_data;
pr_debug("%s[%pK]\n", __func__, audio);
+ mutex_lock(&lock);
mutex_lock(&audio->lock);
mutex_lock(&audio->read_lock);
mutex_lock(&audio->write_lock);
@@ -616,6 +628,8 @@ int audio_aio_release(struct inode *inode, struct file *file)
#endif
kfree(audio->codec_cfg);
kfree(audio);
+ file->private_data = NULL;
+ mutex_unlock(&lock);
return 0;
}
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 9ca73a2b86db..ae54302be8fd 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -327,6 +327,7 @@ void mmc_retune_enable(struct mmc_host *host)
mod_timer(&host->retune_timer,
jiffies + host->retune_period * HZ);
}
+EXPORT_SYMBOL(mmc_retune_enable);
void mmc_retune_disable(struct mmc_host *host)
{
@@ -335,6 +336,7 @@ void mmc_retune_disable(struct mmc_host *host)
host->retune_now = 0;
host->need_retune = 0;
}
+EXPORT_SYMBOL(mmc_retune_disable);
void mmc_retune_timer_stop(struct mmc_host *host)
{
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5b4d5d74fe55..5033107f6e26 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1283,6 +1283,11 @@ static int _mmc_sd_resume(struct mmc_host *host)
#else
err = mmc_sd_init_card(host, host->card->ocr, host->card);
#endif
+ if (err) {
+ pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
mmc_card_clr_suspended(host->card);
if (host->card->sdr104_blocked)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 1eeab7db9722..288d79f4defb 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1823,7 +1823,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
}
pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
- if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))
+ if (gpio_is_valid(pdata->status_gpio) && !(flags & OF_GPIO_ACTIVE_LOW))
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
of_property_read_u32(np, "qcom,bus-width", &bus_width);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 40a34c283955..ddb9947ce298 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2418,7 +2418,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->ops->platform_execute_tuning) {
spin_unlock_irqrestore(&host->lock, flags);
+ /*
+ * Make sure re-tuning won't get triggered for the CRC errors
+ * occurred while executing tuning
+ */
+ mmc_retune_disable(mmc);
err = host->ops->platform_execute_tuning(host, opcode);
+ mmc_retune_enable(mmc);
sdhci_runtime_pm_put(host);
return err;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 9943095abe30..e0200fe50871 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -1008,6 +1008,11 @@ enum ipacm_client_enum ipa2_get_client(int pipe_idx)
*/
bool ipa2_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index d19de2a7bdb5..6647f919a577 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1064,6 +1064,11 @@ enum ipacm_client_enum ipa3_get_client(int pipe_idx)
*/
bool ipa3_get_client_uplink(int pipe_idx)
{
+ if (pipe_idx < 0 || pipe_idx >= IPA3_MAX_NUM_PIPES) {
+ IPAERR("invalid pipe idx %d\n", pipe_idx);
+ return false;
+ }
+
return ipa3_ctx->ipacm_client[pipe_idx].uplink;
}
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1a360caf3fba..544a71e7c242 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -518,7 +518,7 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
-#define UFSHCD_MAX_CMD_LOGGING 100
+#define UFSHCD_MAX_CMD_LOGGING 200
#ifdef CONFIG_TRACEPOINTS
static inline void ufshcd_add_command_trace(struct ufs_hba *hba,
@@ -594,7 +594,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff);
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
int i;
int pos;
@@ -643,7 +643,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
{
}
-static void ufshcd_cmd_log_print(struct ufs_hba *hba)
+static void ufshcd_print_cmd_log(struct ufs_hba *hba)
{
}
#endif
@@ -4301,6 +4301,7 @@ out:
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_host_regs(hba);
+ ufshcd_print_cmd_log(hba);
}
ufshcd_save_tstamp_of_last_dme_cmd(hba);
@@ -6129,7 +6130,7 @@ static void ufshcd_err_handler(struct work_struct *work)
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_tmrs(hba, hba->outstanding_tasks);
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
}
}
@@ -6641,7 +6642,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd->request->tag;
- ufshcd_cmd_log_print(hba);
+ ufshcd_print_cmd_log(hba);
lrbp = &hba->lrb[tag];
err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 78c52cd943bf..565b8f5a1c83 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -168,6 +168,76 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_MAX,
};
+enum icnss_msa_perm {
+ ICNSS_MSA_PERM_HLOS_ALL = 0,
+ ICNSS_MSA_PERM_WLAN_HW_RW = 1,
+ ICNSS_MSA_PERM_DUMP_COLLECT = 2,
+ ICNSS_MSA_PERM_MAX,
+};
+
+#define ICNSS_MAX_VMIDS 4
+
+struct icnss_mem_region_info {
+ uint64_t reg_addr;
+ uint32_t size;
+ uint8_t secure_flag;
+ enum icnss_msa_perm perm;
+};
+
+struct icnss_msa_perm_list_t {
+ int vmids[ICNSS_MAX_VMIDS];
+ int perms[ICNSS_MAX_VMIDS];
+ int nelems;
+};
+
+struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 2,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 3,
+ },
+};
+
+struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
+ [ICNSS_MSA_PERM_HLOS_ALL] = {
+ .vmids = {VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE | PERM_EXEC},
+ .nelems = 1,
+ },
+
+ [ICNSS_MSA_PERM_WLAN_HW_RW] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE},
+ .nelems = 3,
+ },
+
+ [ICNSS_MSA_PERM_DUMP_COLLECT] = {
+ .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
+ .perms = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE,
+ PERM_READ},
+ .nelems = 4,
+ },
+};
+
struct icnss_event_pd_service_down_data {
bool crashed;
bool fw_rejuvenate;
@@ -377,6 +447,84 @@ static void icnss_ignore_qmi_timeout(bool ignore)
static void icnss_ignore_qmi_timeout(bool ignore) { }
#endif
+static int icnss_assign_msa_perm(struct icnss_mem_region_info
+ *mem_region, enum icnss_msa_perm new_perm)
+{
+ int ret = 0;
+ phys_addr_t addr;
+ u32 size;
+ u32 i = 0;
+ u32 source_vmids[ICNSS_MAX_VMIDS];
+ u32 source_nelems;
+ u32 dest_vmids[ICNSS_MAX_VMIDS];
+ u32 dest_perms[ICNSS_MAX_VMIDS];
+ u32 dest_nelems;
+ enum icnss_msa_perm cur_perm = mem_region->perm;
+ struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list;
+
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
+
+ if (mem_region->secure_flag) {
+ new_perm_list = &msa_perm_secure_list[new_perm];
+ old_perm_list = &msa_perm_secure_list[cur_perm];
+ } else {
+ new_perm_list = &msa_perm_list[new_perm];
+ old_perm_list = &msa_perm_list[cur_perm];
+ }
+
+ source_nelems = old_perm_list->nelems;
+ dest_nelems = new_perm_list->nelems;
+
+ for (i = 0; i < source_nelems; ++i)
+ source_vmids[i] = old_perm_list->vmids[i];
+
+ for (i = 0; i < dest_nelems; ++i) {
+ dest_vmids[i] = new_perm_list->vmids[i];
+ dest_perms[i] = new_perm_list->perms[i];
+ }
+
+ ret = hyp_assign_phys(addr, size, source_vmids, source_nelems,
+ dest_vmids, dest_perms, dest_nelems);
+ if (ret) {
+ icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
+ goto out;
+ }
+
+ icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x,"
+ "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n",
+ source_nelems, source_vmids[0], source_vmids[1],
+ source_vmids[2], source_vmids[3], dest_nelems,
+ dest_vmids[0], dest_vmids[1], dest_vmids[2],
+ dest_vmids[3]);
+out:
+ return ret;
+}
+
+static int icnss_assign_msa_perm_all(struct icnss_priv *priv,
+ enum icnss_msa_perm new_perm)
+{
+ int ret;
+ int i;
+ enum icnss_msa_perm old_perm;
+
+ for (i = 0; i < priv->nr_mem_region; i++) {
+ old_perm = priv->mem_region[i].perm;
+ ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
+ if (ret)
+ goto err_unmap;
+ priv->mem_region[i].perm = new_perm;
+ }
+ return 0;
+
+err_unmap:
+ for (i--; i >= 0; i--) {
+ icnss_assign_msa_perm(&priv->mem_region[i], old_perm);
+ }
+ return ret;
+}
+
static void icnss_pm_stay_awake(struct icnss_priv *priv)
{
if (atomic_inc_return(&priv->pm_count) != 1)
@@ -994,119 +1142,6 @@ int icnss_power_off(struct device *dev)
}
EXPORT_SYMBOL(icnss_power_off);
-static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 source_vmlist[1] = {VMID_HLOS};
- int dest_vmids[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[3] = {PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE,
- PERM_READ|PERM_WRITE};
- int source_nelems = sizeof(source_vmlist)/sizeof(u32);
- int dest_nelems = 0;
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- dest_vmids[2] = VMID_WLAN_CE;
- dest_nelems = 3;
- } else {
- dest_vmids[2] = 0;
- dest_nelems = 2;
- }
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
-
- icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
- source_vmlist[0], dest_nelems, dest_vmids[0],
- dest_vmids[1], dest_vmids[2]);
-out:
- return ret;
-
-}
-
-static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
-{
- int ret = 0;
- phys_addr_t addr;
- u32 size;
- u32 dest_vmids[1] = {VMID_HLOS};
- int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
- int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
- int source_nelems = 0;
- int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
-
- addr = mem_region->reg_addr;
- size = mem_region->size;
-
- if (!mem_region->secure_flag) {
- source_vmlist[2] = VMID_WLAN_CE;
- source_nelems = 3;
- } else {
- source_vmlist[2] = 0;
- source_nelems = 2;
- }
-
- ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
- dest_vmids, dest_perms, dest_nelems);
- if (ret) {
- icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
- &addr, size, ret);
- goto out;
- }
- icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
- source_nelems, source_vmlist[0], source_vmlist[1],
- source_vmlist[2], dest_vmids[0]);
-out:
- return ret;
-}
-
-static int icnss_setup_msa_permissions(struct icnss_priv *priv)
-{
- int ret;
- int i;
-
- if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return 0;
-
- for (i = 0; i < priv->nr_mem_region; i++) {
-
- ret = icnss_map_msa_permissions(&priv->mem_region[i]);
- if (ret)
- goto err_unmap;
- }
-
- set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-
- return 0;
-
-err_unmap:
- for (i--; i >= 0; i--)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
- return ret;
-}
-
-static void icnss_remove_msa_permissions(struct icnss_priv *priv)
-{
- int i;
-
- if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
- return;
-
- for (i = 0; i < priv->nr_mem_region; i++)
- icnss_unmap_msa_permissions(&priv->mem_region[i]);
-
- clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
-}
-
static int wlfw_msa_mem_info_send_sync_msg(void)
{
int ret;
@@ -1912,9 +1947,12 @@ static int icnss_driver_event_server_arrive(void *data)
if (ret < 0)
goto err_power_on;
- ret = icnss_setup_msa_permissions(penv);
- if (ret < 0)
- goto err_power_on;
+ if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) {
+ ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW);
+ if (ret < 0)
+ goto err_power_on;
+ set_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
+ }
ret = wlfw_msa_ready_send_sync_msg();
if (ret < 0)
@@ -1932,7 +1970,7 @@ static int icnss_driver_event_server_arrive(void *data)
return ret;
err_setup_msa:
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
err_power_on:
icnss_hw_power_off(penv);
fail:
@@ -2347,14 +2385,22 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
modem_ssr_nb);
struct icnss_uevent_fw_down_data fw_down_data;
+ int ret = 0;
icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
- if (code == SUBSYS_AFTER_SHUTDOWN &&
- notif->crashed == CRASH_STATUS_ERR_FATAL) {
- icnss_remove_msa_permissions(priv);
- icnss_pr_info("Collecting msa0 segment dump\n");
- icnss_msa0_ramdump(priv);
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ ret = icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_DUMP_COLLECT);
+ if (!ret) {
+ icnss_pr_info("Collecting msa0 segment dump\n");
+ icnss_msa0_ramdump(priv);
+ icnss_assign_msa_perm_all(priv,
+ ICNSS_MSA_PERM_WLAN_HW_RW);
+ } else {
+ icnss_pr_err("Not able to Collect msa0 segment dump"
+ "Apps permissions not assigned %d\n", ret);
+ }
return NOTIFY_OK;
}
@@ -4321,7 +4367,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_hw_power_off(penv);
- icnss_remove_msa_permissions(penv);
+ icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL);
+ clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c
index 6691418b516e..63545651fe43 100755
--- a/drivers/soundwire/soundwire.c
+++ b/drivers/soundwire/soundwire.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,27 @@ static void swr_dev_release(struct device *dev)
}
/**
+ * swr_remove_device - remove a soundwire device
+ * @swr_dev: soundwire device to remove
+ *
+ * Remove a soundwire device. Go through the soundwire
+ * device list that master has and remove swr_dev from
+ * it.
+ */
+void swr_remove_device(struct swr_device *swr_dev)
+{
+ struct swr_device *swr_dev_loop, *safe;
+
+ list_for_each_entry_safe(swr_dev_loop, safe,
+ &swr_dev->master->devices,
+ dev_list) {
+ if (swr_dev == swr_dev_loop)
+ list_del(&swr_dev_loop->dev_list);
+ }
+}
+EXPORT_SYMBOL(swr_remove_device);
+
+/**
* swr_new_device - instantiate a new soundwire device
* @master: Controller to which device is connected
* @info: Describes the soundwire device
@@ -129,47 +150,6 @@ err_out:
EXPORT_SYMBOL(swr_new_device);
/**
- * swr_startup_devices - perform additional initialization for child devices
- *
- * @swr_dev: pointer to soundwire slave device
- *
- * Performs any additional initialization needed for a soundwire slave device.
- * This is a optional functionality defined by slave devices.
- * Removes the slave node from the list, in case there is any failure.
- */
-int swr_startup_devices(struct swr_device *swr_dev)
-{
- struct swr_driver *swr_drv;
- struct device *dev;
- int ret = 0;
-
- if (!swr_dev)
- return -EINVAL;
-
- dev = &swr_dev->dev;
- if (!dev)
- return -EINVAL;
-
- swr_drv = to_swr_driver(dev->driver);
- if (!swr_drv)
- return -EINVAL;
-
- if (swr_drv->startup) {
- ret = swr_drv->startup(swr_dev);
- if (ret)
- goto out;
-
- dev_dbg(&swr_dev->dev,
- "%s: startup complete for device %lx\n",
- __func__, swr_dev->addr);
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL(swr_startup_devices);
-
-/**
* of_register_swr_devices - register child devices on to the soundwire bus
* @master: pointer to soundwire master device
*
@@ -203,14 +183,15 @@ int of_register_swr_devices(struct swr_master *master)
}
info.addr = addr;
info.of_node = of_node_get(node);
+ master->num_dev++;
swr = swr_new_device(master, &info);
if (!swr) {
dev_err(&master->dev, "of_swr: Register failed %s\n",
node->full_name);
of_node_put(node);
+ master->num_dev--;
continue;
}
- master->num_dev++;
}
return 0;
}
@@ -610,7 +591,7 @@ int swr_device_up(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_up)
return sdrv->device_up(to_swr_device(dev));
@@ -638,7 +619,7 @@ int swr_device_down(struct swr_device *swr_dev)
dev = &swr_dev->dev;
sdrv = to_swr_driver(dev->driver);
if (!sdrv)
- return -EINVAL;
+ return 0;
if (sdrv->device_down)
return sdrv->device_down(to_swr_device(dev));
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 14c13db991a1..cdaf009c5b1f 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -1369,7 +1369,6 @@ static int swrm_probe(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm;
struct swr_ctrl_platform_data *pdata;
- struct swr_device *swr_dev, *safe;
int ret;
/* Allocate soundwire master driver structure */
@@ -1470,9 +1469,6 @@ static int swrm_probe(struct platform_device *pdev)
goto err_mstr_fail;
}
- if (pdev->dev.of_node)
- of_register_swr_devices(&swrm->master);
-
/* Add devices registered with board-info as the
controller will be up now
*/
@@ -1489,15 +1485,11 @@ static int swrm_probe(struct platform_device *pdev)
}
swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION);
- /* Enumerate slave devices */
- list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
- dev_list) {
- ret = swr_startup_devices(swr_dev);
- if (ret)
- list_del(&swr_dev->dev_list);
- }
mutex_unlock(&swrm->mlock);
+ if (pdev->dev.of_node)
+ of_register_swr_devices(&swrm->master);
+
dbgswrm = swrm;
debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0);
if (!IS_ERR(debugfs_swrm_dent)) {
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 7c35241a487a..b6f4790ffc08 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2015,6 +2015,12 @@ static int ffs_func_eps_enable(struct ffs_function *func)
break;
}
+ /*
+ * userspace setting maxburst > 1 results more fifo
+ * allocation than without maxburst. Change maxburst to 1
+ * only to allocate fifo size of max packet size.
+ */
+ ep->ep->maxburst = 1;
ret = usb_ep_enable(ep->ep);
if (likely(!ret)) {
epfile->ep = ep;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 7d32e2c5cc0d..7ab2fb13061d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -25,6 +25,7 @@ source "drivers/gpu/msm/Kconfig"
source "drivers/gpu/drm/Kconfig"
+source "drivers/video/msm/ba/Kconfig"
menu "Frame buffer Devices"
source "drivers/video/fbdev/Kconfig"
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 1a8c4ced39b2..0a190665a4cf 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm/ba/
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 2b9c71441d68..2f5aad8ed801 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -3493,6 +3493,7 @@ static int __copy_layer_pp_info_igc_params(
compat_ptr(pp_info32->igc_cfg.c0_c1_data);
pp_info->igc_cfg.c2_data =
compat_ptr(pp_info32->igc_cfg.c2_data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3565,6 +3566,7 @@ static int __copy_layer_pp_info_hist_lut_params(
pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len;
pp_info->hist_lut_cfg.data =
compat_ptr(pp_info32->hist_lut_cfg.data);
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3654,6 +3656,7 @@ static int __copy_layer_pp_info_pa_v2_params(
break;
default:
pr_debug("version invalid\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
@@ -3737,6 +3740,7 @@ static int __copy_layer_pp_info_pcc_params(
break;
default:
pr_debug("version invalid, fallback to legacy\n");
+ kfree(cfg_payload);
cfg_payload = NULL;
break;
}
diff --git a/drivers/video/msm/ba/Kconfig b/drivers/video/msm/ba/Kconfig
new file mode 100644
index 000000000000..1ebc7eceeda6
--- /dev/null
+++ b/drivers/video/msm/ba/Kconfig
@@ -0,0 +1,12 @@
+#
+# MSM BA V4L2
+#
+
+config MSM_BA_V4L2
+ tristate "Qualcomm technologies Inc MSM V4L2 based BA driver"
+ depends on VIDEO_V4L2
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Enables support for the MSM V4L2 bridge abstraction
diff --git a/drivers/video/msm/ba/Makefile b/drivers/video/msm/ba/Makefile
new file mode 100644
index 000000000000..b4e7ddf3c79a
--- /dev/null
+++ b/drivers/video/msm/ba/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MSM_BA_V4L2) += msm_v4l2_ba.o \
+ msm_ba_common.o \
+ msm_ba.o \
+ msm_ba_debug.o
+
diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c
new file mode 100644
index 000000000000..3e0838115ca6
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+#define MSM_BA_DEV_NAME "msm_ba_8064"
+
+#define MSM_BA_MAX_EVENTS 10
+
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ poll_wait(filp, &inst->event_handler.wait, wait);
+ if (v4l2_event_pending(&inst->event_handler))
+ rc |= POLLPRI;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_poll);
+
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !cap) {
+ dprintk(BA_ERR,
+ "Invalid input, inst = 0x%p, cap = 0x%p", inst, cap);
+ return -EINVAL;
+ }
+
+ strlcpy(cap->driver, MSM_BA_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MSM_BA_DEV_NAME, sizeof(cap->card));
+ cap->bus_info[0] = 0;
+ cap->version = MSM_BA_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ memset(cap->reserved, 0x00, sizeof(cap->reserved));
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_querycap);
+
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !a)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_parm);
+
+int msm_ba_enum_input(void *instance, struct v4l2_input *input)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !input)
+ return -EINVAL;
+
+ if (input->index >= inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_input(input->index);
+ if (ba_input) {
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL;
+ strlcpy(input->name, ba_input->name, sizeof(input->name));
+ if (ba_input->input_type == BA_INPUT_HDMI ||
+ ba_input->input_type == BA_INPUT_MHL)
+ input->capabilities = V4L2_IN_CAP_CUSTOM_TIMINGS;
+ else
+ input->capabilities = V4L2_IN_CAP_STD;
+ dprintk(BA_DBG, "msm_ba_find_input: name %s", input->name);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_input);
+
+int msm_ba_g_input(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ do {
+ /* First find current input */
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ if (ba_input->input_user_type ==
+ BA_INPUT_USERTYPE_KERNEL) {
+ inst->sd_input.index++;
+ continue;
+ }
+ break;
+ }
+ } while (ba_input);
+
+ if (ba_input)
+ *index = inst->sd_input.index;
+ else
+ rc = -ENOENT;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_input);
+
+int msm_ba_s_input(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+ int rc_sig = 0;
+
+ if (!inst)
+ return -EINVAL;
+ if (index > inst->dev_ctxt->num_inputs)
+ return -EINVAL;
+
+ /* Find requested input */
+ ba_input = msm_ba_find_input(index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d", index);
+ return -EINVAL;
+ }
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (ba_input->in_use &&
+ inst->event_handler.prio == V4L2_PRIORITY_RECORD) {
+ dprintk(BA_WARN, "Input %d in use", index);
+ return -EBUSY;
+ }
+ if (ba_input->ba_out_in_use) {
+ if (inst->ext_ops) {
+ if (inst->restore) {
+ dprintk(BA_DBG, "Stream off in set input: %d",
+ ba_input->bridge_chip_ip);
+ rc_sig = v4l2_subdev_call(ba_input->sd,
+ video, s_stream, 0);
+ if (rc_sig)
+ dprintk(BA_ERR,
+ "%s: Error in stream off. rc_sig %d",
+ __func__, rc_sig);
+ }
+ } else {
+ dprintk(BA_WARN, "Sd %d in use", ba_input->ba_out);
+ return -EBUSY;
+ }
+ }
+ rc = v4l2_subdev_call(ba_input->sd, video, s_routing,
+ ba_input->bridge_chip_ip, 0, 0);
+ if (rc) {
+ dprintk(BA_ERR, "Error: %d setting input: %d",
+ rc, ba_input->bridge_chip_ip);
+ return rc;
+ }
+ msm_ba_reset_ip_in_use_from_sd(ba_input->sd);
+ inst->sd_input.index = index;
+ strlcpy(inst->sd_input.name, ba_input->name,
+ sizeof(inst->sd_input.name));
+ inst->sd = ba_input->sd;
+ ba_input->in_use = 1;
+ /* get current signal status */
+ rc_sig = v4l2_subdev_call(
+ ba_input->sd, video, g_input_status, &ba_input->signal_status);
+ dprintk(BA_DBG, "Set input %s : %d - signal status: %d",
+ ba_input->name, index, ba_input->signal_status);
+ if (!rc_sig && !ba_input->signal_status) {
+ struct v4l2_event sd_event = {
+ .id = 0,
+ .type = V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK};
+ int *ptr = (int *)sd_event.u.data;
+ ptr[0] = index;
+ ptr[1] = ba_input->signal_status;
+ msm_ba_queue_v4l2_event(inst, &sd_event);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_input);
+
+int msm_ba_enum_output(void *instance, struct v4l2_output *output)
+{
+ struct msm_ba_input *ba_input = NULL;
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !output)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(output->index);
+ if (!ba_input)
+ return -EINVAL;
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->std = V4L2_STD_ALL;
+ strlcpy(output->name, ba_input->sd->name, sizeof(output->name));
+ output->capabilities = V4L2_OUT_CAP_STD;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_enum_output);
+
+int msm_ba_g_output(void *instance, unsigned int *index)
+{
+ struct msm_ba_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !index)
+ return -EINVAL;
+
+ *index = inst->sd_output.index;
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_output);
+
+int msm_ba_s_output(void *instance, unsigned int index)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ ba_input = msm_ba_find_output(index);
+ if (ba_input) {
+ if (!ba_input->sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input->ba_out = index;
+ inst->sd_output.index = index;
+ inst->sd = ba_input->sd;
+ inst->sd_input.index = ba_input->ba_ip_idx;
+ } else {
+ dprintk(BA_ERR, "Could not find output index: %d", index);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_s_output);
+
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_enum_fmt);
+
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_fmt);
+
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct msm_ba_input *ba_input = NULL;
+ v4l2_std_id new_std = V4L2_STD_UNKNOWN;
+ struct v4l2_dv_timings sd_dv_timings;
+ struct v4l2_subdev_format sd_fmt;
+ int rc = 0;
+
+ if (!inst || !f)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (!ba_input) {
+ dprintk(BA_ERR, "Could not find input index: %d",
+ inst->sd_input.index);
+ return -EINVAL;
+ }
+ if (ba_input->input_type != BA_INPUT_HDMI) {
+ rc = v4l2_subdev_call(sd, video, querystd, &new_std);
+ if (rc) {
+ dprintk(BA_ERR, "querystd failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ inst->sd_input.std = new_std;
+ } else {
+ rc = v4l2_subdev_call(sd, video, g_dv_timings, &sd_dv_timings);
+ if (rc) {
+ dprintk(BA_ERR, "g_dv_timings failed %d for sd: %s",
+ rc, sd->name);
+ return -EINVAL;
+ }
+ }
+
+ rc = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (rc) {
+ dprintk(BA_ERR, "get_fmt failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ f->fmt.pix.height = sd_fmt.format.height;
+ f->fmt.pix.width = sd_fmt.format.width;
+ switch (sd_fmt.format.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_VYUY;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown sd_mbus_fmt.code 0x%x",
+ sd_fmt.format.code);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ break;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_g_fmt);
+
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_s_ctrl(NULL, &inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_s_ctrl);
+
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return v4l2_g_ctrl(&inst->ctrl_handler, control);
+}
+EXPORT_SYMBOL(msm_ba_g_ctrl);
+
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
+{
+ struct msm_ba_inst *inst = instance;
+
+ if (!inst || !control)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(msm_ba_s_ext_ctrl);
+
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (rc)
+ dprintk(BA_ERR, "Stream on failed on input: %d",
+ inst->sd_input.index);
+ else
+ msm_ba_set_out_in_use(sd, 1);
+
+ dprintk(BA_DBG, "Stream on: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamon);
+
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (rc)
+ dprintk(BA_ERR, "Stream off failed on input: %d",
+ inst->sd_input.index);
+
+ dprintk(BA_DBG, "Stream off: %s : %d",
+ inst->sd_input.name, inst->sd_input.index);
+ msm_ba_set_out_in_use(sd, 0);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_streamoff);
+
+long msm_ba_private_ioctl(void *instance, int cmd, void *arg)
+{
+ long rc = 0;
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ int *s_ioctl = arg;
+
+ dprintk(BA_DBG, "Enter %s with command: 0x%x", __func__, cmd);
+
+ if (!inst)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_HDMI_RX_CEC_S_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_PHYSICAL");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_G_CONNECTED: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_CONNECTED");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ case VIDIOC_HDMI_RX_CEC_S_ENABLE: {
+ dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_ENABLE");
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ if (s_ioctl) {
+ rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl);
+ if (rc)
+ dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x",
+ __func__, rc, cmd);
+ } else {
+ dprintk(BA_ERR, "%s: NULL argument provided", __func__);
+ rc = -EINVAL;
+ }
+ }
+ break;
+ default:
+ dprintk(BA_WARN, "Not a typewriter! Command: 0x%x", cmd);
+ rc = -ENOTTY;
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_private_ioctl);
+
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ if (sr == BA_SR_RESTORE_IP &&
+ inst->restore) {
+ dprintk(BA_DBG, "Restoring input: %d",
+ inst->saved_input);
+ rc = v4l2_subdev_call(inst->sd, video, s_routing,
+ inst->saved_input, 0, 0);
+ if (rc)
+ dprintk(BA_ERR, "Failed to restore input: %d",
+ inst->saved_input);
+ msm_ba_reset_ip_in_use_from_sd(inst->sd);
+ ba_input = msm_ba_find_input_from_sd(inst->sd,
+ inst->saved_input);
+ if (ba_input)
+ ba_input->in_use = 1;
+ else
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ inst->saved_input, inst->sd->name);
+ inst->restore = 0;
+ inst->saved_input = BA_IP_MAX;
+ dprintk(BA_DBG, "Stream on from save restore");
+ rc = msm_ba_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ } else if (sr == BA_SR_SAVE_IP) {
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input == NULL) {
+ dprintk(BA_ERR, "Could not find input %d",
+ inst->sd_input.index);
+ } else if (ba_input->ba_out_in_use) {
+ inst->restore = 1;
+ inst->saved_input =
+ msm_ba_find_ip_in_use_from_sd(inst->sd);
+ if (inst->saved_input == BA_IP_MAX) {
+ dprintk(BA_ERR, "Could not find input to save");
+ inst->restore = 0;
+ }
+ dprintk(BA_DBG, "Saving input: %d",
+ inst->saved_input);
+ rc = -EBUSY;
+ }
+ } else {
+ dprintk(BA_DBG, "Nothing to do in save and restore");
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_save_restore_input);
+
+void msm_ba_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+static int msm_ba_register_v4l2_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+ struct video_device *vdev;
+ int rc = 0;
+
+ dprintk(BA_DBG, "Enter %s: v4l2_dev 0x%p, v4l2_subdev 0x%p",
+ __func__, v4l2_dev, sd);
+ if (NULL == v4l2_dev || NULL == sd || !sd->name[0]) {
+ dprintk(BA_ERR, "Invalid input");
+ return -EINVAL;
+ }
+ rc = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (rc < 0) {
+ dprintk(BA_ERR,
+ "%s(%d), V4L2 subdev register failed for %s rc: %d",
+ __func__, __LINE__, sd->name, rc);
+ return rc;
+ }
+ if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) {
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ dprintk(BA_ERR, "%s Not enough memory", __func__);
+ return -ENOMEM;
+ }
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_ba_release_subdev_node;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s Error registering video device %s",
+ __func__, sd->name);
+ kfree(vdev);
+ } else {
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.dev.major = VIDEO_MAJOR;
+ sd->entity.info.dev.minor = vdev->minor;
+ sd->entity.name = video_device_node_name(vdev);
+#endif
+ sd->devnode = vdev;
+ }
+ }
+ dprintk(BA_DBG, "Exit %s with rc: %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ rc = msm_ba_register_v4l2_subdev(&ba_ctxt->dev_ctxt->v4l2_dev, sd);
+ if (!rc) {
+ ba_ctxt->dev_ctxt->num_ba_subdevs++;
+ msm_ba_add_inputs(sd);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_register_subdev_node);
+
+static void __msm_ba_sd_unregister(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ mutex_lock(&ba_ctxt->ba_cs);
+
+ v4l2_device_unregister_subdev(sub_dev);
+ ba_ctxt->dev_ctxt->num_ba_subdevs--;
+ msm_ba_del_inputs(sub_dev);
+
+ dprintk(BA_DBG, "%s(%d), BA Unreg Sub Device : num ba devices %d : %s",
+ __func__, __LINE__,
+ ba_ctxt->dev_ctxt->num_ba_subdevs, sub_dev->name);
+
+ mutex_unlock(&ba_ctxt->ba_cs);
+}
+
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sub_dev)
+{
+ struct ba_ctxt *ba_ctxt;
+
+ ba_ctxt = msm_ba_get_ba_context();
+ if (!ba_ctxt || !ba_ctxt->dev_ctxt)
+ return -ENODEV;
+ if (!sub_dev)
+ return -EINVAL;
+ __msm_ba_sd_unregister(sub_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ba_unregister_subdev_node);
+
+static int msm_ba_setup_event_queue(void *inst,
+ struct video_device *pvdev)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ v4l2_fh_init(&ba_inst->event_handler, pvdev);
+ v4l2_fh_add(&ba_inst->event_handler);
+
+ return rc;
+}
+
+int msm_ba_subscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_subscribe(&ba_inst->event_handler, sub,
+ MSM_BA_MAX_EVENTS, NULL);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_subscribe_event);
+
+int msm_ba_unsubscribe_event(void *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst;
+
+ if (!inst || !sub)
+ return -EINVAL;
+
+ rc = v4l2_event_unsubscribe(&ba_inst->event_handler, sub);
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_unsubscribe_event);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input *ba_input;
+ struct msm_ba_sd_event *ba_sd_event;
+ int bridge_chip_ip;
+
+ if (!sd || !arg) {
+ dprintk(BA_ERR, "%s null v4l2 subdev or arg", __func__);
+ return;
+ }
+
+ bridge_chip_ip = ((int *)((struct v4l2_event *)arg)->u.data)[0];
+ ba_input = msm_ba_find_input_from_sd(sd, bridge_chip_ip);
+ if (!ba_input) {
+ dprintk(BA_WARN, "Could not find input %d from sd: %s",
+ bridge_chip_ip, sd->name);
+ return;
+ }
+
+ ba_sd_event = kzalloc(sizeof(*ba_sd_event), GFP_KERNEL);
+ if (!ba_sd_event) {
+ dprintk(BA_ERR, "%s out of memory", __func__);
+ return;
+ }
+
+ dev_ctxt = get_ba_dev();
+
+ ba_sd_event->sd_event = *(struct v4l2_event *)arg;
+ ((int *)ba_sd_event->sd_event.u.data)[0] = ba_input->ba_ip_idx;
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&ba_sd_event->list, &dev_ctxt->sd_events);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ schedule_delayed_work(&dev_ctxt->sd_events_work, 0);
+}
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int rc = 0;
+
+ dev_ctxt = get_ba_dev();
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+
+ if (!inst) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ return NULL;
+ }
+
+ mutex_init(&inst->inst_cs);
+
+ init_waitqueue_head(&inst->kernel_event_queue);
+ inst->state = MSM_BA_DEV_UNINIT_DONE;
+ inst->dev_ctxt = dev_ctxt;
+ rc = msm_ba_ctrl_init(inst);
+ if (rc) {
+ dprintk(BA_WARN, "Failed to initialize controls: %d", rc);
+ msm_ba_ctrl_deinit(inst);
+ }
+
+ if (!list_empty(&(inst->dev_ctxt->v4l2_dev.subdevs)))
+ inst->sd = list_first_entry(&(inst->dev_ctxt->v4l2_dev.subdevs),
+ struct v4l2_subdev, list);
+
+ msm_ba_setup_event_queue(inst, dev_ctxt->vdev);
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ list_add_tail(&inst->list, &dev_ctxt->instances);
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_INIT;
+ dev_ctxt->state = BA_DEV_INIT_DONE;
+ inst->state = MSM_BA_DEV_INIT_DONE;
+ inst->sd_input.index = 0;
+ inst->event_handler.prio = V4L2_PRIORITY_DEFAULT;
+
+ inst->debugfs_root =
+ msm_ba_debugfs_init_inst(inst, dev_ctxt->debugfs_root);
+
+ inst->ext_ops = ext_ops;
+
+ return inst;
+}
+EXPORT_SYMBOL(msm_ba_open);
+
+int msm_ba_close(void *instance)
+{
+ struct msm_ba_inst *inst = instance;
+ struct msm_ba_inst *temp;
+ struct msm_ba_dev *dev_ctxt;
+ struct list_head *ptr;
+ struct list_head *next;
+ int rc = 0;
+
+ if (!inst)
+ return -EINVAL;
+
+ dev_ctxt = inst->dev_ctxt;
+ mutex_lock(&dev_ctxt->dev_cs);
+
+ list_for_each_safe(ptr, next, &dev_ctxt->instances) {
+ temp = list_entry(ptr, struct msm_ba_inst, list);
+ if (temp == inst)
+ list_del(&inst->list);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+
+ msm_ba_ctrl_deinit(inst);
+
+ v4l2_fh_del(&inst->event_handler);
+ v4l2_fh_exit(&inst->event_handler);
+
+ debugfs_remove_recursive(inst->debugfs_root);
+
+ dprintk(BA_DBG, "Closed BA instance: %p", inst);
+ kfree(inst);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_ba_close);
diff --git a/drivers/video/msm/ba/msm_ba_common.c b/drivers/video/msm/ba/msm_ba_common.c
new file mode 100644
index 000000000000..cc8eb2da3e3b
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.c
@@ -0,0 +1,645 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/jiffies.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#include "msm_ba_debug.h"
+#include "msm_ba_common.h"
+
+static struct msm_ba_ctrl msm_ba_ctrls[] = {
+ {
+ .id = MSM_BA_PRIV_SD_NODE_ADDR,
+ .name = "Sub-device Node Address",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 16,
+ .default_value = 0,
+ .step = 2,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+ {
+ .id = MSM_BA_PRIV_FPS,
+ .name = "FPS in Q16 format",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 0x7fffffff,
+ .default_value = 60 << 16,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .qmenu = NULL,
+ },
+};
+
+#define BA_NUM_CTRLS ARRAY_SIZE(msm_ba_ctrls)
+
+/* Assuming den is not zero, max 32 bits */
+#define BA_FRAC_TO_Q16(q, num, den) { \
+ uint32_t pwr; \
+ pwr = ilog2(den); \
+ (q) = (num) << (16 - pwr); \
+ }
+
+struct msm_ba_dev *get_ba_dev(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ mutex_lock(&ba_ctxt->ba_cs);
+ dev_ctxt = ba_ctxt->dev_ctxt;
+ mutex_unlock(&ba_ctxt->ba_cs);
+
+ return dev_ctxt;
+}
+
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event)
+{
+ v4l2_event_queue_fh(&inst->event_handler, sd_event);
+ wake_up(&inst->kernel_event_queue);
+}
+
+static void msm_ba_print_event(struct v4l2_event *sd_event)
+{
+ switch (sd_event->type) {
+ case V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED:
+ dprintk(BA_DBG, "Port settings changed for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK:
+ dprintk(BA_DBG, "Signal in lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK:
+ dprintk(BA_DBG, "Signal lost lock for ip_idx %d",
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_SOURCE_CHANGE:
+ dprintk(BA_DBG, "Video source change 0x%x",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_HPD:
+ dprintk(BA_DBG, "HDMI hotplug detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE:
+ dprintk(BA_DBG, "HDMI CEC message!");
+ break;
+ case V4L2_EVENT_MSM_BA_CP:
+ dprintk(BA_DBG, "Content protection detected!");
+ break;
+ case V4L2_EVENT_MSM_BA_CABLE_DETECT:
+ dprintk(BA_DBG, "Cable detected: %d on ip_idx %d",
+ ((int *)sd_event->u.data)[1],
+ ((int *)sd_event->u.data)[0]);
+ break;
+ case V4L2_EVENT_MSM_BA_ERROR:
+ dprintk(BA_DBG, "Subdev error %d!",
+ ((int *)sd_event->u.data)[1]);
+ break;
+ default:
+ dprintk(BA_ERR, "Unknown event: 0x%x", sd_event->type);
+ break;
+ }
+}
+
+static void msm_ba_signal_sessions_event(struct v4l2_event *sd_event)
+{
+ struct msm_ba_inst *inst = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int *ptr;
+
+ msm_ba_print_event(sd_event);
+ dev_ctxt = get_ba_dev();
+ ptr = (int *)sd_event->u.data;
+
+ list_for_each_entry(inst, &(dev_ctxt->instances), list) {
+ if (inst->ext_ops && inst->ext_ops->msm_ba_cb)
+ inst->ext_ops->msm_ba_cb(
+ inst, sd_event->id, (void *)&ptr[1]);
+ else
+ msm_ba_queue_v4l2_event(inst, sd_event);
+ }
+}
+
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work)
+{
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ mutex_lock(&dev_ctxt->dev_cs);
+ if (!list_empty(&dev_ctxt->sd_events)) {
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &(dev_ctxt->sd_events), list) {
+ msm_ba_signal_sessions_event(&ba_sd_event->sd_event);
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ break;
+ }
+ } else {
+ dprintk(BA_ERR, "%s - queue empty!!!", __func__);
+ }
+ mutex_unlock(&dev_ctxt->dev_cs);
+}
+
+struct v4l2_subdev *msm_ba_sd_find(const char *name)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev *sd_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&(dev_ctxt->v4l2_dev.subdevs))) {
+ list_for_each_entry(sd, &(dev_ctxt->v4l2_dev.subdevs), list)
+ if (!strcmp(name, sd->name)) {
+ sd_out = sd;
+ break;
+ }
+ }
+ return sd_out;
+}
+
+void msm_ba_add_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct msm_ba_input_config *msm_ba_inp_cfg = NULL;
+ int i;
+ int str_length = 0;
+ int rc;
+ int start_index = 0;
+ int end_index = 0;
+ int dev_id = 0;
+
+ dev_ctxt = get_ba_dev();
+ if (!list_empty(&dev_ctxt->inputs))
+ start_index = dev_ctxt->num_inputs;
+
+ msm_ba_inp_cfg = dev_ctxt->msm_ba_inp_cfg;
+ dev_id = msm_ba_inp_cfg[start_index].ba_out;
+ end_index = dev_ctxt->num_config_inputs;
+ for (i = start_index; i < end_index; i++) {
+ str_length = strlen(msm_ba_inp_cfg[i].sd_name);
+ rc = memcmp(sd->name, msm_ba_inp_cfg[i].sd_name, str_length);
+ if (!rc && dev_id == msm_ba_inp_cfg[i].ba_out) {
+ input = kzalloc(sizeof(*input), GFP_KERNEL);
+
+ if (!input) {
+ dprintk(BA_ERR, "Failed to allocate memory");
+ break;
+ }
+ input->input_type = msm_ba_inp_cfg[i].input_type;
+ strlcpy(input->name, msm_ba_inp_cfg[i].name,
+ sizeof(input->name));
+ input->bridge_chip_ip = msm_ba_inp_cfg[i].ba_ip;
+ input->ba_out = msm_ba_inp_cfg[i].ba_out;
+ input->ba_node_addr = msm_ba_inp_cfg[i].ba_node;
+ input->ba_ip_idx = i;
+ input->input_user_type =
+ msm_ba_inp_cfg[i].input_user_type;
+ input->sd = sd;
+ list_add_tail(&input->list, &dev_ctxt->inputs);
+ dev_ctxt->num_inputs++;
+ dprintk(BA_DBG, "Add input: name %s on %d",
+ input->name, input->ba_out);
+ }
+ }
+}
+
+void msm_ba_del_inputs(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct list_head *ptr;
+ struct list_head *next;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ list_for_each_safe(ptr, next, &(dev_ctxt->inputs)) {
+ input = list_entry(ptr, struct msm_ba_input, list);
+ if (input->sd == sd) {
+ list_del(&input->list);
+ kfree(input);
+ }
+ }
+}
+
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd)
+ input->ba_out_in_use = on;
+ }
+}
+
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ int ba_ip = BA_IP_MAX;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ ba_ip = input->bridge_chip_ip;
+ break;
+ }
+ }
+ return ba_ip;
+}
+
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->in_use) {
+ input->in_use = 0;
+ break;
+ }
+ }
+}
+
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->sd == sd &&
+ input->bridge_chip_ip == bridge_chip_ip) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list)
+ if (input->ba_ip_idx == ba_input_idx) {
+ input_out = input;
+ break;
+ }
+ }
+ return input_out;
+}
+
+struct msm_ba_input *msm_ba_find_output(int ba_output)
+{
+ struct msm_ba_input *input = NULL;
+ struct msm_ba_input *input_out = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+
+ dev_ctxt = get_ba_dev();
+
+ if (!list_empty(&(dev_ctxt->inputs))) {
+ list_for_each_entry(input, &(dev_ctxt->inputs), list) {
+ if (input->ba_out == ba_output) {
+ input_out = input;
+ break;
+ }
+ }
+ }
+ return input_out;
+}
+
+int msm_ba_g_fps(void *instance, int *fps_q16)
+{
+ struct msm_ba_inst *inst = instance;
+ struct v4l2_subdev *sd = NULL;
+ struct v4l2_subdev_frame_interval sd_frame_int;
+ int rc = 0;
+
+ if (!inst || !fps_q16)
+ return -EINVAL;
+
+ sd = inst->sd;
+ if (!sd) {
+ dprintk(BA_ERR, "No sd registered");
+ return -EINVAL;
+ }
+ rc = v4l2_subdev_call(sd, video, g_frame_interval, &sd_frame_int);
+ if (rc) {
+ dprintk(BA_ERR, "get frame interval failed %d for sd: %s",
+ rc, sd->name);
+ } else {
+ /* subdevice returns frame interval not fps! */
+ if (sd_frame_int.interval.numerator) {
+ BA_FRAC_TO_Q16(*fps_q16,
+ sd_frame_int.interval.denominator,
+ sd_frame_int.interval.numerator);
+ } else {
+ *fps_q16 =
+ sd_frame_int.interval.denominator << 16;
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_try_get_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ctrl->val = ba_input->ba_node_addr;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x ctrl->val:%d",
+ __func__, ctrl->id, ctrl->val);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ case MSM_BA_PRIV_FPS:
+ rc = msm_ba_g_fps(inst, &ctrl->val);
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_try_set_ctrl(struct msm_ba_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ struct msm_ba_input *ba_input = NULL;
+ int rc = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ ba_input = msm_ba_find_input(inst->sd_input.index);
+ if (ba_input) {
+ ba_input->ba_node_addr = ctrl->val;
+ dprintk(BA_DBG,
+ "%s: SD NODE ADDR ctrl->id:0x%x node_addr:%d",
+ __func__, ctrl->id, ba_input->ba_node_addr);
+ } else {
+ dprintk(BA_ERR, "%s Could not find input",
+ __func__);
+ rc = -EINVAL;
+ }
+ break;
+ default:
+ dprintk(BA_ERR, "%s id: 0x%x not supported",
+ __func__, ctrl->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int msm_ba_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ if (!inst) {
+ dprintk(BA_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ for (c = 0; c < ctrl->ncontrols; ++c) {
+ if (ctrl->cluster[c]->is_new) {
+ rc = msm_ba_try_set_ctrl(inst, ctrl->cluster[c]);
+ if (rc) {
+ dprintk(BA_ERR, "Failed setting 0x%x",
+ ctrl->cluster[c]->id);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int msm_ba_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int rc = 0;
+ int c = 0;
+ struct msm_ba_inst *inst = container_of(ctrl->handler,
+ struct msm_ba_inst, ctrl_handler);
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ for (c = 0; c < master->ncontrols; c++) {
+ if (master->cluster[c]->id == ctrl->id) {
+ rc = msm_ba_try_get_ctrl(inst, ctrl);
+ if (rc) {
+ dprintk(BA_ERR, "Failed getting 0x%x",
+ ctrl->id);
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+static const struct v4l2_ctrl_ops msm_ba_ctrl_ops = {
+
+ .g_volatile_ctrl = msm_ba_op_g_volatile_ctrl,
+ .s_ctrl = msm_ba_op_s_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_ba_get_ctrl_ops(void)
+{
+ return &msm_ba_ctrl_ops;
+}
+
+static struct v4l2_ctrl **msm_ba_get_super_cluster(struct msm_ba_inst *inst,
+ int *size)
+{
+ int c = 0;
+ int sz = 0;
+ struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
+ BA_NUM_CTRLS, GFP_KERNEL);
+
+ if (!size || !cluster || !inst)
+ return NULL;
+
+ for (c = 0; c < BA_NUM_CTRLS; c++)
+ cluster[sz++] = inst->ctrls[c];
+
+ *size = sz;
+ return cluster;
+}
+
+/*
+ * Controls init function.
+ * Caller is expected to call deinit in case of failure.
+ */
+int msm_ba_ctrl_init(struct msm_ba_inst *inst)
+{
+ int idx = 0;
+ struct v4l2_ctrl_config ctrl_cfg = {0};
+ int rc = 0;
+ int cluster_size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "%s - invalid instance", __func__);
+ return -EINVAL;
+ }
+
+ inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * BA_NUM_CTRLS,
+ GFP_KERNEL);
+ if (!inst->ctrls) {
+ dprintk(BA_ERR, "%s - failed to allocate ctrl", __func__);
+ return -ENOMEM;
+ }
+
+ rc = v4l2_ctrl_handler_init(&inst->ctrl_handler, BA_NUM_CTRLS);
+
+ if (rc) {
+ dprintk(BA_ERR, "CTRL ERR: Control handler init failed, %d",
+ inst->ctrl_handler.error);
+ return rc;
+ }
+ for (; idx < BA_NUM_CTRLS; idx++) {
+ struct v4l2_ctrl *ctrl = NULL;
+ if (BA_IS_PRIV_CTRL(msm_ba_ctrls[idx].id)) {
+ /* add private control */
+ ctrl_cfg.def = msm_ba_ctrls[idx].default_value;
+ ctrl_cfg.flags = 0;
+ ctrl_cfg.id = msm_ba_ctrls[idx].id;
+ ctrl_cfg.max = msm_ba_ctrls[idx].maximum;
+ ctrl_cfg.min = msm_ba_ctrls[idx].minimum;
+ ctrl_cfg.menu_skip_mask =
+ msm_ba_ctrls[idx].menu_skip_mask;
+ ctrl_cfg.name = msm_ba_ctrls[idx].name;
+ ctrl_cfg.ops = &msm_ba_ctrl_ops;
+ ctrl_cfg.step = msm_ba_ctrls[idx].step;
+ ctrl_cfg.type = msm_ba_ctrls[idx].type;
+ ctrl_cfg.qmenu = msm_ba_ctrls[idx].qmenu;
+
+ ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+ &ctrl_cfg, NULL);
+ } else {
+ if (msm_ba_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(
+ &inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].menu_skip_mask,
+ msm_ba_ctrls[idx].default_value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &msm_ba_ctrl_ops,
+ msm_ba_ctrls[idx].id,
+ msm_ba_ctrls[idx].minimum,
+ msm_ba_ctrls[idx].maximum,
+ msm_ba_ctrls[idx].step,
+ msm_ba_ctrls[idx].default_value);
+ }
+ }
+
+ if (!ctrl) {
+ dprintk(BA_ERR, "%s - invalid ctrl", __func__);
+ return -EINVAL;
+ }
+
+ rc = inst->ctrl_handler.error;
+ if (rc) {
+ dprintk(BA_ERR,
+ "Error adding ctrl (%s) to ctrl handle, %d",
+ msm_ba_ctrls[idx].name,
+ inst->ctrl_handler.error);
+ return rc;
+ }
+
+ switch (msm_ba_ctrls[idx].id) {
+ case MSM_BA_PRIV_SD_NODE_ADDR:
+ case MSM_BA_PRIV_FPS:
+ ctrl->flags |= msm_ba_ctrls[idx].flags;
+ break;
+ }
+
+ inst->ctrls[idx] = ctrl;
+ }
+
+ /* Construct a super cluster of all controls */
+ inst->cluster = msm_ba_get_super_cluster(inst, &cluster_size);
+ if (!inst->cluster || !cluster_size) {
+ dprintk(BA_WARN,
+ "Failed to setup super cluster");
+ return -EINVAL;
+ }
+ v4l2_ctrl_cluster(cluster_size, inst->cluster);
+
+ return rc;
+}
+
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst)
+{
+ kfree(inst->ctrls);
+ kfree(inst->cluster);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+}
diff --git a/drivers/video/msm/ba/msm_ba_common.h b/drivers/video/msm/ba/msm_ba_common.h
new file mode 100644
index 000000000000..64ef2ab7537e
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_common.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2016, 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.
+ *
+ */
+
+#ifndef _MSM_BA_COMMON_H_
+#define _MSM_BA_COMMON_H_
+
+#include "msm_ba_internal.h"
+
+#define BA_IS_PRIV_CTRL(idx) (\
+ (V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_USER) && \
+ V4L2_CTRL_DRIVER_PRIV(idx))
+
+struct msm_ba_dev *get_ba_dev(void);
+void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst,
+ struct v4l2_event *sd_event);
+struct v4l2_subdev *msm_ba_sd_find(const char *name);
+void msm_ba_add_inputs(struct v4l2_subdev *sd);
+void msm_ba_del_inputs(struct v4l2_subdev *sd);
+void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on);
+int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd);
+void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd);
+struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd,
+ int bridge_chip_ip);
+struct msm_ba_input *msm_ba_find_input(int ba_input_idx);
+struct msm_ba_input *msm_ba_find_output(int ba_output);
+int msm_ba_g_fps(void *instance, int *fps_q16);
+int msm_ba_ctrl_init(struct msm_ba_inst *inst);
+void msm_ba_ctrl_deinit(struct msm_ba_inst *inst);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_debug.c b/drivers/video/msm/ba/msm_ba_debug.c
new file mode 100644
index 000000000000..aa5109eb8e64
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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_ba_debug.h"
+
+#define MAX_DBG_BUF_SIZE 1008
+
+int msm_ba_debug = BA_ERR | BA_WARN;
+int msm_ba_debug_out = BA_OUT_PRINTK;
+
+struct debug_buffer {
+ char ptr[MAX_DBG_BUF_SIZE];
+ char *curr;
+ u32 filled_size;
+};
+
+#define INIT_DBG_BUF(__buf) ({ \
+ __buf->curr = __buf->ptr;\
+ __buf->filled_size = 0; \
+})
+
+static int dev_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+{
+ va_list args;
+ u32 size = 0;
+ size_t buf_size = 0;
+
+ if (MAX_DBG_BUF_SIZE - 1 > buffer->filled_size) {
+ buf_size = MAX_DBG_BUF_SIZE - 1 - buffer->filled_size;
+ va_start(args, fmt);
+ size = vscnprintf(buffer->curr, buf_size, fmt, args);
+ va_end(args);
+ buffer->curr += size;
+ buffer->filled_size += size;
+ }
+ return size;
+}
+
+static ssize_t dev_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_dev *dev_ctxt = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, dev: 0x%p", dev_ctxt);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "DEV: 0x%p", dev_ctxt);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "state: %d", dev_ctxt->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations dev_info_fops = {
+ .open = dev_info_open,
+ .read = dev_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_drv(void)
+{
+ bool ok = false;
+ struct dentry *dir = debugfs_create_dir(BA_DBG_LABEL, NULL);
+ struct ba_ctxt *ba_ctxt;
+
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ goto failed_create_dir;
+ }
+
+#define __debugfs_create(__type, __name, __value) ({ \
+ struct dentry *f = debugfs_create_##__type(__name, S_IRUGO | S_IWUSR, \
+ dir, __value); \
+ if (IS_ERR_OR_NULL(f)) { \
+ dprintk(BA_ERR, "Failed creating debugfs file '%pd/%s'", \
+ dir, __name); \
+ f = NULL; \
+ } \
+ f; \
+})
+
+ ok =
+ __debugfs_create(x32, "debug_level", &msm_ba_debug) &&
+ __debugfs_create(u32, "debug_output", &msm_ba_debug_out);
+
+#undef __debugfs_create
+
+ if (!ok)
+ goto failed_create_dir;
+
+ return dir;
+
+failed_create_dir:
+ if (dir) {
+ ba_ctxt = msm_ba_get_ba_context();
+ debugfs_remove_recursive(ba_ctxt->debugfs_root);
+ }
+ return NULL;
+}
+
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!dev_ctxt) {
+ dprintk(BA_ERR, "Invalid params, core: %p", dev_ctxt);
+ goto failed_create_dir;
+ }
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "dev_%p", dev_ctxt);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, dev_ctxt,
+ &dev_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+failed_create_dir:
+ return dir;
+}
+
+static int inst_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t inst_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_ba_inst *inst = file->private_data;
+ struct debug_buffer *dbg_buf = NULL;
+ ssize_t size = 0;
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, dev: %p", inst);
+ return 0;
+ }
+
+ dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ if (dbg_buf == NULL)
+ return 0;
+
+ INIT_DBG_BUF(dbg_buf);
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "INSTANCE: %p (%s)", inst,
+ "BA device");
+ write_str(dbg_buf, "===============================");
+ write_str(dbg_buf, "dev: %p", inst->dev_ctxt);
+ write_str(dbg_buf, "state: %d", inst->state);
+
+ size = simple_read_from_buffer(buf, count, ppos,
+ dbg_buf->ptr, dbg_buf->filled_size);
+
+ kfree(dbg_buf);
+
+ return size;
+}
+
+static const struct file_operations inst_info_fops = {
+ .open = inst_info_open,
+ .read = inst_info_read,
+};
+
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+
+ if (!inst) {
+ dprintk(BA_ERR, "Invalid params, inst: %p", inst);
+ goto failed_create_dir;
+ }
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (!dir) {
+ dprintk(BA_ERR, "Failed to create debugfs for msm_ba");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) {
+ dprintk(BA_ERR, "debugfs_create_file: fail");
+ goto failed_create_dir;
+ }
+ inst->debug.pdata[SESSION_INIT].sampling = true;
+failed_create_dir:
+ return dir;
+}
diff --git a/drivers/video/msm/ba/msm_ba_debug.h b/drivers/video/msm/ba/msm_ba_debug.h
new file mode 100644
index 000000000000..baabb712cc58
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_debug.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012-2015, 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.
+ *
+ */
+
+#ifndef __MSM_BA_DEBUG__
+#define __MSM_BA_DEBUG__
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include "msm_ba_internal.h"
+
+#ifndef BA_DBG_LABEL
+#define BA_DBG_LABEL "msm_ba"
+#endif
+
+#define BA_DBG_TAG BA_DBG_LABEL "(%d): %4s: "
+
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x001F
+ */
+
+enum ba_msg_prio {
+ BA_ERR = 0x0001,
+ BA_WARN = 0x0002,
+ BA_INFO = 0x0004,
+ BA_DBG = 0x0008,
+ BA_PROF = 0x0010
+};
+
+enum ba_msg_out {
+ BA_OUT_PRINTK = 0,
+ BA_OUT_FTRACE
+};
+
+extern int msm_ba_debug;
+extern int msm_ba_debug_out;
+
+#define BA_MSG_PRIO2STRING(__level) ({ \
+ char *__str; \
+ \
+ __str = (__level == BA_ERR ? "err" : \
+ (__level == BA_WARN ? "warn" : \
+ (__level == BA_INFO ? "info" : \
+ (__level == BA_DBG ? "dbg" : \
+ (__level == BA_PROF ? "prof" : "????"))))); \
+ \
+ __str; \
+ })
+
+#define dprintk(__level, __fmt, arg...) \
+ do { \
+ if (msm_ba_debug & __level) { \
+ if (msm_ba_debug_out == BA_OUT_PRINTK) { \
+ pr_info(BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } else if (msm_ba_debug_out == BA_OUT_FTRACE) { \
+ trace_printk(KERN_DEBUG BA_DBG_TAG __fmt "\n", \
+ __LINE__, \
+ BA_MSG_PRIO2STRING(__level), \
+ ## arg); \
+ } \
+ } \
+ } while (0)
+
+
+struct dentry *msm_ba_debugfs_init_drv(void);
+struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt,
+ struct dentry *parent);
+struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst,
+ struct dentry *parent);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_ba_internal.h b/drivers/video/msm/ba/msm_ba_internal.h
new file mode 100644
index 000000000000..bd52e8e400ce
--- /dev/null
+++ b/drivers/video/msm/ba/msm_ba_internal.h
@@ -0,0 +1,220 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_BA_INTERNAL_H_
+#define _MSM_BA_INTERNAL_H_
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_ba.h>
+
+#define MSM_BA_DRV_NAME "msm_ba_driver"
+
+#define MSM_BA_VERSION KERNEL_VERSION(0, 0, 1)
+
+#define MAX_NAME_LENGTH 64
+
+#define MAX_DEBUGFS_NAME MAX_NAME_LENGTH
+
+#define DEFAULT_WIDTH 720
+#define DEFAULT_HEIGHT 507
+
+enum ba_dev_state {
+ BA_DEV_UNINIT = 0,
+ BA_DEV_LOADED,
+ BA_DEV_INIT,
+ BA_DEV_INIT_DONE,
+ BA_DEV_INVALID
+};
+
+enum instance_state {
+ MSM_BA_DEV_UNINIT_DONE = 0x0001,
+ MSM_BA_DEV_INIT,
+ MSM_BA_DEV_INIT_DONE,
+ MSM_BA_OPEN,
+ MSM_BA_OPEN_DONE,
+ MSM_BA_START,
+ MSM_BA_START_DONE,
+ MSM_BA_STOP,
+ MSM_BA_STOP_DONE,
+ MSM_BA_CLOSE,
+ MSM_BA_CLOSE_DONE,
+ MSM_BA_DEV_UNINIT,
+ MSM_BA_DEV_INVALID
+};
+
+struct ba_ctxt {
+ struct mutex ba_cs;
+ struct msm_ba_dev *dev_ctxt;
+ struct dentry *debugfs_root;
+};
+
+enum profiling_points {
+ SYS_INIT = 0,
+ SESSION_INIT,
+ MAX_PROFILING_POINTS
+};
+
+struct profile_data {
+ int start;
+ int stop;
+ int cumulative;
+ char name[64];
+ int sampling;
+ int average;
+};
+
+struct msm_ba_debug {
+ struct profile_data pdata[MAX_PROFILING_POINTS];
+ int profile;
+ int samples;
+};
+
+struct msm_ba_dev_capability {
+ u32 capability_set;
+};
+
+enum msm_ba_ip_type {
+ BA_INPUT_CVBS = 0,
+ BA_INPUT_COMPONENT,
+ BA_INPUT_YC,
+ BA_INPUT_RGB,
+ BA_INPUT_HDMI,
+ BA_INPUT_MHL,
+ BA_INPUT_DVI,
+ BA_INPUT_TTL,
+ BA_INPUT_MAX = 0xffffffff
+};
+
+enum msm_ba_input_usr_type {
+ BA_INPUT_USERTYPE_KERNEL = 0,
+ BA_INPUT_USERTYPE_USER,
+ BA_INPUT_USERTYPE_MAX = 0xffffffff
+};
+
+struct msm_ba_input_config {
+ enum msm_ba_ip_type input_type;
+ const char *name;
+ int ba_ip;
+ int ba_out;
+ const char *sd_name;
+ int ba_node;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_sd_event {
+ struct list_head list;
+ struct v4l2_event sd_event;
+};
+
+struct msm_ba_input {
+ struct list_head list;
+ enum msm_ba_ip_type input_type;
+ unsigned int name_index;
+ char name[32];
+ int bridge_chip_ip;
+ int ba_node_addr;
+ int ba_out;
+ int ba_ip_idx;
+ struct v4l2_subdev *sd;
+ int signal_status;
+ int in_use;
+ int ba_out_in_use;
+ enum msm_ba_input_usr_type input_user_type;
+};
+
+struct msm_ba_dev {
+ struct mutex dev_cs;
+
+ enum ba_dev_state state;
+
+ struct list_head inputs;
+ uint32_t num_inputs;
+
+ /* V4L2 Framework */
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct media_device mdev;
+
+ struct list_head instances;
+
+ /* BA v4l2 sub devs */
+ uint32_t num_ba_subdevs;
+ struct list_head sd_events;
+ struct delayed_work sd_events_work;
+
+ /* BA input config list */
+ struct msm_ba_input_config *msm_ba_inp_cfg;
+ uint32_t num_config_inputs;
+
+ struct dentry *debugfs_root;
+};
+
+struct msm_ba_inst {
+ struct list_head list;
+ struct mutex inst_cs;
+ struct msm_ba_dev *dev_ctxt;
+
+ struct v4l2_input sd_input;
+ struct v4l2_output sd_output;
+ struct v4l2_subdev *sd;
+ int state;
+ int saved_input;
+ int restore;
+
+ struct v4l2_fh event_handler;
+ wait_queue_head_t kernel_event_queue;
+
+ struct v4l2_ctrl **cluster;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl **ctrls;
+
+ struct msm_ba_debug debug;
+ struct dentry *debugfs_root;
+
+ const struct msm_ba_ext_ops *ext_ops;
+};
+
+struct msm_ba_ctrl {
+ u32 id;
+ char name[MAX_NAME_LENGTH];
+ enum v4l2_ctrl_type type;
+ s32 minimum;
+ s32 maximum;
+ s32 default_value;
+ u32 step;
+ u32 menu_skip_mask;
+ u32 flags;
+ const char * const *qmenu;
+};
+
+struct ba_ctxt *msm_ba_get_ba_context(void);
+
+void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg);
+void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work);
+
+#endif
diff --git a/drivers/video/msm/ba/msm_v4l2_ba.c b/drivers/video/msm/ba/msm_v4l2_ba.c
new file mode 100644
index 000000000000..0cd3fc3b238f
--- /dev/null
+++ b/drivers/video/msm/ba/msm_v4l2_ba.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/msm_ba.h>
+
+#include "msm_ba_internal.h"
+#include "msm_ba_debug.h"
+
+#define BASE_DEVICE_NUMBER 35
+
+static struct ba_ctxt *gp_ba_ctxt;
+
+struct ba_ctxt *msm_ba_get_ba_context(void)
+{
+ return gp_ba_ctxt;
+}
+
+void msm_ba_set_ba_context(struct ba_ctxt *ba_ctxt)
+{
+ gp_ba_ctxt = ba_ctxt;
+}
+
+static inline struct msm_ba_inst *get_ba_inst(struct file *filp, void *fh)
+{
+ return container_of(filp->private_data,
+ struct msm_ba_inst, event_handler);
+}
+
+static int msm_ba_v4l2_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = msm_ba_open(NULL);
+ if (!ba_inst) {
+ dprintk(BA_ERR,
+ "Failed to create video instance");
+ return -ENOMEM;
+ }
+ clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags);
+ filp->private_data = &(ba_inst->event_handler);
+ return 0;
+}
+
+static int msm_ba_v4l2_close(struct file *filp)
+{
+ int rc = 0;
+ struct msm_ba_inst *ba_inst;
+
+ ba_inst = get_ba_inst(filp, NULL);
+
+ rc = msm_ba_close(ba_inst);
+ return rc;
+}
+
+static int msm_ba_v4l2_querycap(struct file *filp, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, fh);
+
+ return msm_ba_querycap((void *)ba_inst, cap);
+}
+
+int msm_ba_v4l2_enum_input(struct file *file, void *fh,
+ struct v4l2_input *input)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_input((void *)ba_inst, input);
+}
+
+int msm_ba_v4l2_g_input(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_input(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_input((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_output(struct file *file, void *fh,
+ struct v4l2_output *output)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_output((void *)ba_inst, output);
+}
+
+int msm_ba_v4l2_g_output(struct file *file, void *fh,
+ unsigned int *index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_s_output(struct file *file, void *fh,
+ unsigned int index)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_output((void *)ba_inst, index);
+}
+
+int msm_ba_v4l2_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_enum_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_fmt((void *)ba_inst, f);
+}
+
+int msm_ba_v4l2_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_g_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_s_ext_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_ext_ctrl((void *)ba_inst, a);
+}
+
+int msm_ba_v4l2_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamon((void *)ba_inst, i);
+}
+
+int msm_ba_v4l2_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type i)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_streamoff((void *)ba_inst, i);
+}
+
+static int msm_ba_v4l2_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_subscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct msm_ba_inst *ba_inst = container_of(fh,
+ struct msm_ba_inst, event_handler);
+
+ return msm_ba_unsubscribe_event((void *)ba_inst, sub);
+}
+
+static int msm_ba_v4l2_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(file, fh);
+
+ return msm_ba_s_parm((void *)ba_inst, a);
+}
+
+static int msm_ba_v4l2_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = {
+ .vidioc_querycap = msm_ba_v4l2_querycap,
+ .vidioc_enum_fmt_vid_cap = msm_ba_v4l2_enum_fmt,
+ .vidioc_enum_fmt_vid_out = msm_ba_v4l2_enum_fmt,
+ .vidioc_s_fmt_vid_cap = msm_ba_v4l2_s_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = msm_ba_v4l2_s_fmt,
+ .vidioc_g_fmt_vid_cap = msm_ba_v4l2_g_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = msm_ba_v4l2_g_fmt,
+ .vidioc_streamon = msm_ba_v4l2_streamon,
+ .vidioc_streamoff = msm_ba_v4l2_streamoff,
+ .vidioc_s_ctrl = msm_ba_v4l2_s_ctrl,
+ .vidioc_g_ctrl = msm_ba_v4l2_g_ctrl,
+ .vidioc_s_ext_ctrls = msm_ba_v4l2_s_ext_ctrl,
+ .vidioc_subscribe_event = msm_ba_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = msm_ba_v4l2_unsubscribe_event,
+ .vidioc_s_parm = msm_ba_v4l2_s_parm,
+ .vidioc_g_parm = msm_ba_v4l2_g_parm,
+ .vidioc_enum_input = msm_ba_v4l2_enum_input,
+ .vidioc_g_input = msm_ba_v4l2_g_input,
+ .vidioc_s_input = msm_ba_v4l2_s_input,
+ .vidioc_enum_output = msm_ba_v4l2_enum_output,
+ .vidioc_g_output = msm_ba_v4l2_g_output,
+ .vidioc_s_output = msm_ba_v4l2_s_output,
+};
+
+static unsigned int msm_ba_v4l2_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct msm_ba_inst *ba_inst = get_ba_inst(filp, NULL);
+
+ return msm_ba_poll((void *)ba_inst, filp, pt);
+}
+
+void msm_ba_release_video_device(struct video_device *pvdev)
+{
+}
+
+static const struct v4l2_file_operations msm_ba_v4l2_ba_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_ba_v4l2_open,
+ .release = msm_ba_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = msm_ba_v4l2_poll,
+};
+
+static int parse_ba_dt(struct platform_device *pdev)
+{
+ uint32_t profile_count = 0;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child_np = NULL;
+ struct msm_ba_dev *dev_ctxt = NULL;
+ struct ba_ctxt *ba_ctxt = msm_ba_get_ba_context();
+ char *key = NULL;
+ uint32_t err = 0, i = 0;
+
+ dev_ctxt = ba_ctxt->dev_ctxt;
+
+ profile_count = of_get_child_count(np);
+ if (profile_count == 0) {
+ dprintk(BA_ERR, "%s: Error reading DT. node=%s",
+ __func__, np->full_name);
+ return -ENODEV;
+ }
+
+ dev_ctxt->msm_ba_inp_cfg = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_ba_input_config) * profile_count,
+ GFP_KERNEL);
+ if (!dev_ctxt->msm_ba_inp_cfg)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(np, child_np) {
+ key = "qcom,type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_type);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-input";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_ip);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-output";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_out);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,sd-name";
+ err = of_property_read_string(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].sd_name);
+ if (err)
+ goto read_fail;
+
+ key = "qcom,ba-node";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].ba_node);
+ if (err)
+ goto read_fail;
+
+
+ key = "qcom,user-type";
+ err = of_property_read_u32(child_np, key,
+ &dev_ctxt->msm_ba_inp_cfg[i].input_user_type);
+ if (err)
+ goto read_fail;
+
+ i++;
+ }
+ dev_ctxt->num_config_inputs = i;
+
+read_fail:
+ if (err) {
+ dprintk(BA_INFO, "%s: Error reading DT. node=%s key=%s",
+ __func__, np->full_name, key);
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+
+ dev_ctxt->num_config_inputs = 0;
+ }
+
+ return err;
+}
+
+static int msm_ba_device_init(struct platform_device *pdev,
+ struct msm_ba_dev **ret_dev_ctxt)
+{
+ struct msm_ba_dev *dev_ctxt;
+ int nr = BASE_DEVICE_NUMBER;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ if ((ret_dev_ctxt == NULL) ||
+ (*ret_dev_ctxt != NULL) ||
+ (pdev == NULL)) {
+ dprintk(BA_ERR, "%s(%d) Invalid params %p %p %p",
+ __func__, __LINE__,
+ ret_dev_ctxt, *ret_dev_ctxt, pdev);
+ return -EINVAL;
+ }
+
+ dev_ctxt = devm_kzalloc(&pdev->dev, sizeof(struct msm_ba_dev),
+ GFP_KERNEL);
+ if (dev_ctxt == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dev_ctxt);
+
+ INIT_LIST_HEAD(&dev_ctxt->inputs);
+ INIT_LIST_HEAD(&dev_ctxt->instances);
+ INIT_LIST_HEAD(&dev_ctxt->sd_events);
+ INIT_DELAYED_WORK(&dev_ctxt->sd_events_work,
+ msm_ba_subdev_event_hndlr_delayed);
+ mutex_init(&dev_ctxt->dev_cs);
+
+ dev_ctxt->state = BA_DEV_UNINIT;
+
+ strlcpy(dev_ctxt->v4l2_dev.name, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->v4l2_dev.name));
+ dev_ctxt->v4l2_dev.dev = &pdev->dev;
+ dev_ctxt->v4l2_dev.notify = msm_ba_subdev_event_hndlr;
+
+ rc = v4l2_device_register(dev_ctxt->v4l2_dev.dev, &dev_ctxt->v4l2_dev);
+ if (!rc) {
+ dev_ctxt->vdev = video_device_alloc();
+ if (dev_ctxt->vdev == NULL) {
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ rc = -ENOMEM;
+ } else {
+ strlcpy(dev_ctxt->vdev->name,
+ pdev->name, sizeof(dev_ctxt->vdev->name));
+ dev_ctxt->vdev->v4l2_dev = &dev_ctxt->v4l2_dev;
+ dev_ctxt->vdev->release = msm_ba_release_video_device;
+ dev_ctxt->vdev->fops = &msm_ba_v4l2_ba_fops;
+ dev_ctxt->vdev->ioctl_ops = &msm_ba_v4l2_ioctl_ops;
+ dev_ctxt->vdev->minor = nr;
+ dev_ctxt->vdev->vfl_type = VFL_TYPE_GRABBER;
+
+ video_set_drvdata(dev_ctxt->vdev, &dev_ctxt);
+
+ strlcpy(dev_ctxt->mdev.model, MSM_BA_DRV_NAME,
+ sizeof(dev_ctxt->mdev.model));
+ dev_ctxt->mdev.dev = &pdev->dev;
+ rc = media_device_register(&dev_ctxt->mdev);
+ dev_ctxt->v4l2_dev.mdev = &dev_ctxt->mdev;
+ rc = media_entity_init(&dev_ctxt->vdev->entity,
+ 0, NULL, 0);
+ dev_ctxt->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ dev_ctxt->vdev->entity.group_id = 2;
+
+ rc = video_register_device(dev_ctxt->vdev,
+ VFL_TYPE_GRABBER, nr);
+ if (!rc) {
+ dev_ctxt->vdev->entity.name =
+ video_device_node_name(dev_ctxt->vdev);
+ *ret_dev_ctxt = dev_ctxt;
+ } else {
+ dprintk(BA_ERR,
+ "Failed to register BA video device");
+ }
+ }
+ } else {
+ dprintk(BA_ERR, "Failed to register v4l2 device");
+ }
+
+ if (rc) {
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_probe(struct platform_device *pdev)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s: pdev %p device id = %d",
+ __func__, pdev, pdev->id);
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context not yet created");
+ return -EINVAL;
+ }
+ rc = msm_ba_device_init(pdev, &ba_ctxt->dev_ctxt);
+ if (rc)
+ dprintk(BA_ERR, "Failed to init device");
+ else
+ ba_ctxt->dev_ctxt->debugfs_root = msm_ba_debugfs_init_dev(
+ ba_ctxt->dev_ctxt, ba_ctxt->debugfs_root);
+
+ rc = parse_ba_dt(pdev);
+ if (rc < 0) {
+ dprintk(BA_ERR, "%s: devicetree error. Exit init", __func__);
+ return rc;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static int msm_ba_remove(struct platform_device *pdev)
+{
+ struct msm_ba_dev *dev_ctxt = platform_get_drvdata(pdev);
+ struct msm_ba_sd_event *ba_sd_event = NULL;
+ struct msm_ba_sd_event *ba_sd_event_tmp = NULL;
+ int rc = 0;
+
+ if (dev_ctxt == NULL) {
+ dprintk(BA_ERR, "%s invalid device", __func__);
+ rc = -EINVAL;
+ } else {
+ video_unregister_device(dev_ctxt->vdev);
+ v4l2_device_unregister(&dev_ctxt->v4l2_dev);
+ cancel_delayed_work_sync(&dev_ctxt->sd_events_work);
+ list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp,
+ &dev_ctxt->sd_events, list) {
+ list_del(&ba_sd_event->list);
+ kfree(ba_sd_event);
+ }
+
+ devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg);
+ devm_kfree(&pdev->dev, dev_ctxt);
+ dev_ctxt = NULL;
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+int msm_ba_create(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt != NULL) {
+ dprintk(BA_ERR, "BA context already created");
+ return -EINVAL;
+ }
+ ba_ctxt = kzalloc(sizeof(struct ba_ctxt), GFP_KERNEL);
+
+ if (ba_ctxt == NULL)
+ return -ENOMEM;
+
+ memset(ba_ctxt, 0x00, sizeof(struct ba_ctxt));
+
+ mutex_init(&ba_ctxt->ba_cs);
+ ba_ctxt->debugfs_root = msm_ba_debugfs_init_drv();
+ if (!ba_ctxt->debugfs_root)
+ dprintk(BA_ERR,
+ "Failed to create debugfs for msm_ba");
+
+ msm_ba_set_ba_context(ba_ctxt);
+
+ dprintk(BA_DBG, "%s(%d), BA create complete",
+ __func__, __LINE__);
+
+ return rc;
+}
+
+int msm_ba_destroy(void)
+{
+ struct ba_ctxt *ba_ctxt;
+ int rc = 0;
+
+ ba_ctxt = msm_ba_get_ba_context();
+
+ if (ba_ctxt == NULL) {
+ dprintk(BA_ERR, "BA context non existent");
+ return -EINVAL;
+ }
+
+ if (ba_ctxt->dev_ctxt != NULL) {
+ dprintk(BA_ERR, "Device instances exist on BA context");
+ return -EBUSY;
+ }
+ mutex_destroy(&ba_ctxt->ba_cs);
+
+ kfree(ba_ctxt);
+ ba_ctxt = NULL;
+ msm_ba_set_ba_context(ba_ctxt);
+
+ return rc;
+}
+
+static const struct of_device_id msm_ba_dt_match[] = {
+ {.compatible = "qcom,msm-ba"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ba_dt_match);
+
+static struct platform_driver msm_ba_driver = {
+ .probe = msm_ba_probe,
+ .remove = msm_ba_remove,
+ .driver = {
+ .name = "msm_ba_v4l2",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ba_dt_match,
+ },
+};
+
+static int __init msm_ba_mod_init(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ rc = msm_ba_create();
+ if (!rc) {
+ rc = platform_driver_register(&msm_ba_driver);
+ if (rc) {
+ dprintk(BA_ERR,
+ "Failed to register platform driver");
+ msm_ba_destroy();
+ }
+ }
+ dprintk(BA_INFO, "Exit %s with error %d", __func__, rc);
+
+ return rc;
+}
+
+static void __exit msm_ba_mod_exit(void)
+{
+ int rc = 0;
+
+ dprintk(BA_INFO, "Enter %s", __func__);
+ platform_driver_unregister(&msm_ba_driver);
+ rc = msm_ba_destroy();
+ dprintk(BA_INFO, "Exit %s", __func__);
+}
+
+module_init(msm_ba_mod_init);
+module_exit(msm_ba_mod_exit);
+
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 279411c42ded..aea4c0f2ef5f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -823,6 +823,8 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
}
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
void mmc_retune_timer_stop(struct mmc_host *host);
static inline void mmc_retune_needed(struct mmc_host *host)
diff --git a/include/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h
index 2083e7b5da25..1287d2b73bf8 100755
--- a/include/linux/soundwire/soundwire.h
+++ b/include/linux/soundwire/soundwire.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,7 +196,6 @@ static inline struct swr_device *to_swr_device(struct device *dev)
* @shutdown: standard shutdown callback used during power down/halt
* @suspend: standard suspend callback used during system suspend
* @resume: standard resume callback used during system resume
- * @startup: additional init operation for slave devices
* @driver: soundwire device drivers should initialize name and
* owner field of this structure
* @id_table: list of soundwire devices supported by this driver
@@ -210,7 +209,6 @@ struct swr_driver {
int (*device_up)(struct swr_device *swr);
int (*device_down)(struct swr_device *swr);
int (*reset_device)(struct swr_device *swr);
- int (*startup)(struct swr_device *swr);
struct device_driver driver;
const struct swr_device_id *id_table;
};
@@ -309,4 +307,6 @@ extern int swr_reset_device(struct swr_device *swr_dev);
extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num,
bool enable);
extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num);
+
+extern void swr_remove_device(struct swr_device *swr_dev);
#endif /* _LINUX_SOUNDWIRE_H */
diff --git a/include/media/msm_ba.h b/include/media/msm_ba.h
new file mode 100644
index 000000000000..1b51e3f754d8
--- /dev/null
+++ b/include/media/msm_ba.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2012-2015, 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.
+ *
+ */
+
+#ifndef _MSM_BA_H_
+#define _MSM_BA_H_
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <linux/poll.h>
+
+enum msm_ba_ip {
+ BA_IP_CVBS_0 = 0,
+ BA_IP_CVBS_1,
+ BA_IP_CVBS_2,
+ BA_IP_CVBS_3,
+ BA_IP_CVBS_4,
+ BA_IP_CVBS_5,
+ BA_IP_SVIDEO_0,
+ BA_IP_SVIDEO_1,
+ BA_IP_SVIDEO_2,
+ BA_IP_COMPONENT_0,
+ BA_IP_COMPONENT_1,
+ BA_IP_DVI_0,
+ BA_IP_DVI_1,
+ BA_IP_HDMI_1,
+ BA_IP_MHL_1,
+ BA_IP_TTL,
+ BA_IP_MAX = 0xffffffff
+};
+
+enum msm_ba_save_restore_ip {
+ BA_SR_RESTORE_IP = 0,
+ BA_SR_SAVE_IP,
+ BA_SR_MAX = 0xffffffff
+};
+
+struct msm_ba_ext_ops {
+ void (*msm_ba_cb)(void *instance,
+ unsigned int event_id, void *arg);
+};
+
+void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops);
+int msm_ba_close(void *instance);
+int msm_ba_querycap(void *instance, struct v4l2_capability *cap);
+int msm_ba_g_priority(void *instance, enum v4l2_priority *prio);
+int msm_ba_s_priority(void *instance, enum v4l2_priority prio);
+int msm_ba_enum_input(void *instance, struct v4l2_input *input);
+int msm_ba_g_input(void *instance, unsigned int *index);
+int msm_ba_s_input(void *instance, unsigned int index);
+int msm_ba_enum_output(void *instance, struct v4l2_output *output);
+int msm_ba_g_output(void *instance, unsigned int *index);
+int msm_ba_s_output(void *instance, unsigned int index);
+int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_ba_s_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_g_fmt(void *instance, struct v4l2_format *f);
+int msm_ba_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_ba_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_ba_streamon(void *instance, enum v4l2_buf_type i);
+int msm_ba_streamoff(void *instance, enum v4l2_buf_type i);
+int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr);
+int msm_ba_poll(void *instance, struct file *filp,
+ struct poll_table_struct *pt);
+int msm_ba_subscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_unsubscribe_event(void *instance,
+ const struct v4l2_event_subscription *sub);
+int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a);
+int msm_ba_register_subdev_node(struct v4l2_subdev *sd);
+int msm_ba_unregister_subdev_node(struct v4l2_subdev *sd);
+#endif
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 0764b9e26962..241ddd176636 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -83,13 +83,6 @@ struct icnss_wlan_enable_cfg {
struct icnss_shadow_reg_cfg *shadow_reg_cfg;
};
-/* MSA Memory Regions Information */
-struct icnss_mem_region_info {
- uint64_t reg_addr;
- uint32_t size;
- uint8_t secure_flag;
-};
-
/* driver modes */
enum icnss_driver_mode {
ICNSS_MISSION,
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 336d318c5187..2e4c02f24a47 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1331,6 +1331,12 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_PAN_SPEED (V4L2_CID_CAMERA_CLASS_BASE+32)
#define V4L2_CID_TILT_SPEED (V4L2_CID_CAMERA_CLASS_BASE+33)
+/* User-class control IDs specific to the msm_ba driver */
+
+#define MSM_BA_PRIV_BASE_START (V4L2_CID_USER_BASE | 0x7000)
+#define MSM_BA_PRIV_SD_NODE_ADDR (MSM_BA_PRIV_BASE_START + 1)
+#define MSM_BA_PRIV_FPS (MSM_BA_PRIV_BASE_START + 2)
+
/* FM Modulator class control IDs */
#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 686fc6143010..bb2c4ebf9ff4 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2187,6 +2187,31 @@ struct v4l2_streamparm {
#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9)
#define V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED (V4L2_EVENT_MSM_VIDC_START + 10)
+#define V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE \
+ (V4L2_EVENT_PRIVATE_START + 0x00005000)
+#define V4L2_EVENT_MSM_BA_START V4L2_EVENT_MSM_BA_PRIVATE_EVENT_BASE
+#define V4L2_EVENT_MSM_BA_DEVICE_AVAILABLE (V4L2_EVENT_MSM_BA_START + 1)
+#define V4L2_EVENT_MSM_BA_DEVICE_UNAVAILABLE \
+ (V4L2_EVENT_MSM_BA_START + 2)
+#define V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED \
+ (V4L2_EVENT_MSM_BA_START + 3)
+#define V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 4)
+#define V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK \
+ (V4L2_EVENT_MSM_BA_START + 5)
+#define V4L2_EVENT_MSM_BA_SOURCE_CHANGE \
+ (V4L2_EVENT_MSM_BA_START + 6)
+#define V4L2_EVENT_MSM_BA_HDMI_HPD \
+ (V4L2_EVENT_MSM_BA_START + 7)
+#define V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE \
+ (V4L2_EVENT_MSM_BA_START + 8)
+#define V4L2_EVENT_MSM_BA_CP \
+ (V4L2_EVENT_MSM_BA_START + 9)
+#define V4L2_EVENT_MSM_BA_CABLE_DETECT \
+ (V4L2_EVENT_MSM_BA_START + 10)
+#define V4L2_EVENT_MSM_BA_ERROR \
+ (V4L2_EVENT_MSM_BA_START + 11)
+
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
/* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
@@ -2442,4 +2467,11 @@ struct v4l2_create_buffers {
#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */
+/* HDMI rx provide ioctls */
+#define VIDIOC_HDMI_RX_CEC_S_LOGICAL _IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
+#define VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL _IO('V', BASE_VIDIOC_PRIVATE + 1)
+#define VIDIOC_HDMI_RX_CEC_G_PHYSICAL _IOR('V', BASE_VIDIOC_PRIVATE + 2, int)
+#define VIDIOC_HDMI_RX_CEC_G_CONNECTED _IOR('V', BASE_VIDIOC_PRIVATE + 3, int)
+#define VIDIOC_HDMI_RX_CEC_S_ENABLE _IOR('V', BASE_VIDIOC_PRIVATE + 4, int)
+
#endif /* _UAPI__LINUX_VIDEODEV2_H */
diff --git a/net/core/dev.c b/net/core/dev.c
index 2587d7f30191..57922df9c250 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4552,6 +4552,19 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb)
}
EXPORT_SYMBOL(__skb_gro_checksum_complete);
+static void net_rps_send_ipi(struct softnet_data *remsd)
+{
+#ifdef CONFIG_RPS
+ while (remsd) {
+ struct softnet_data *next = remsd->rps_ipi_next;
+
+ if (cpu_online(remsd->cpu))
+ smp_call_function_single_async(remsd->cpu, &remsd->csd);
+ remsd = next;
+ }
+#endif
+}
+
/*
* net_rps_action_and_irq_enable sends any pending IPI's for rps.
* Note: called with local irq disabled, but exits with local irq enabled.
@@ -4567,20 +4580,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
local_irq_enable();
/* Send pending IPI's to kick RPS processing on remote cpus. */
- while (remsd) {
- struct softnet_data *next = remsd->rps_ipi_next;
-
- if (cpu_online(remsd->cpu)) {
- smp_call_function_single_async(remsd->cpu,
- &remsd->csd);
- } else {
- pr_err("%s() cpu offline\n", __func__);
- rps_lock(remsd);
- remsd->backlog.state = 0;
- rps_unlock(remsd);
- }
- remsd = next;
- }
+ net_rps_send_ipi(remsd);
} else
#endif
local_irq_enable();
@@ -7495,7 +7495,7 @@ static int dev_cpu_callback(struct notifier_block *nfb,
struct sk_buff **list_skb;
struct sk_buff *skb;
unsigned int cpu, oldcpu = (unsigned long)ocpu;
- struct softnet_data *sd, *oldsd;
+ struct softnet_data *sd, *oldsd, *remsd;
if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
return NOTIFY_OK;
@@ -7539,6 +7539,13 @@ static int dev_cpu_callback(struct notifier_block *nfb,
raise_softirq_irqoff(NET_TX_SOFTIRQ);
local_irq_enable();
+#ifdef CONFIG_RPS
+ remsd = oldsd->rps_ipi_list;
+ oldsd->rps_ipi_list = NULL;
+#endif
+ /* send out pending IPI's on offline CPU */
+ net_rps_send_ipi(remsd);
+
/* Process offline CPU's input_pkt_queue */
while ((skb = __skb_dequeue(&oldsd->process_queue))) {
netif_rx_ni(skb);
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index eaaca97e2b8e..0af656ce48f0 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1123,54 +1123,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
.get_regmap = wsa881x_get_regmap,
};
-static int wsa881x_swr_startup(struct swr_device *swr_dev)
-{
- int ret = 0;
- u8 devnum = 0;
- struct wsa881x_priv *wsa881x;
-
- wsa881x = swr_get_dev_data(swr_dev);
- if (!wsa881x) {
- dev_err(&swr_dev->dev, "%s: wsa881x is NULL\n", __func__);
- return -EINVAL;
- }
-
- /*
- * Add 5msec delay to provide sufficient time for
- * soundwire auto enumeration of slave devices as
- * as per HW requirement.
- */
- usleep_range(5000, 5010);
- ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
- if (ret) {
- dev_dbg(&swr_dev->dev,
- "%s get devnum %d for dev addr %lx failed\n",
- __func__, devnum, swr_dev->addr);
- goto err;
- }
- swr_dev->dev_num = devnum;
-
- wsa881x->regmap = devm_regmap_init_swr(swr_dev,
- &wsa881x_regmap_config);
- if (IS_ERR(wsa881x->regmap)) {
- ret = PTR_ERR(wsa881x->regmap);
- dev_err(&swr_dev->dev, "%s: regmap_init failed %d\n",
- __func__, ret);
- goto err;
- }
-
- ret = snd_soc_register_codec(&swr_dev->dev, &soc_codec_dev_wsa881x,
- NULL, 0);
- if (ret) {
- dev_err(&swr_dev->dev, "%s: Codec registration failed\n",
- __func__);
- goto err;
- }
-
-err:
- return ret;
-}
-
static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable)
{
int ret = 0;
@@ -1232,6 +1184,7 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
{
int ret = 0;
struct wsa881x_priv *wsa881x;
+ u8 devnum = 0;
wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
GFP_KERNEL);
@@ -1291,8 +1244,43 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
&codec_debug_ops);
}
}
+
+ /*
+ * Add 5msec delay to provide sufficient time for
+ * soundwire auto enumeration of slave devices as
+ * as per HW requirement.
+ */
+ usleep_range(5000, 5010);
+ ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s get devnum %d for dev addr %lx failed\n",
+ __func__, devnum, pdev->addr);
+ goto dev_err;
+ }
+ pdev->dev_num = devnum;
+
+ wsa881x->regmap = devm_regmap_init_swr(pdev,
+ &wsa881x_regmap_config);
+ if (IS_ERR(wsa881x->regmap)) {
+ ret = PTR_ERR(wsa881x->regmap);
+ dev_err(&pdev->dev, "%s: regmap_init failed %d\n",
+ __func__, ret);
+ goto dev_err;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed\n",
+ __func__);
+ goto dev_err;
+ }
+
return 0;
+dev_err:
+ swr_remove_device(pdev);
err:
return ret;
}
@@ -1425,7 +1413,6 @@ static struct swr_driver wsa881x_codec_driver = {
.device_up = wsa881x_swr_up,
.device_down = wsa881x_swr_down,
.reset_device = wsa881x_swr_reset,
- .startup = wsa881x_swr_startup,
};
static int __init wsa881x_codec_init(void)
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index a71fb74d35bc..6b1c150f1ee4 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -283,6 +283,7 @@ static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
*dai_data->status_mask);
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ memset(&dai_data->ca, 0, sizeof(dai_data->ca));
}