diff options
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)); } |