diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-07-11 00:27:31 -0600 |
|---|---|---|
| committer | Linux Build Service Account <lnxbuild@localhost> | 2016-07-11 00:27:31 -0600 |
| commit | a36d8398690233636488ffbebbc5bae5349e784d (patch) | |
| tree | 79141c268d73f7fda14143161215324583cf33fd | |
| parent | 5b900dd89d6ecb861d24f82cc9bdd6b250e5b1fc (diff) | |
| parent | 1f1574bb1837ce7ee0bf24c40a178cdcbf2932da (diff) | |
Promotion of kernel.lnx.4.4-160707.
CRs Change ID Subject
--------------------------------------------------------------------------------------------------------------
1003482 I959caf7b305e965b84e8204168194bbfda72dc52 mfd: wcd9xxx-irq: check if wcd9xxx_res irq is 0 before u
950797 I5ee7d44d6cad8e35e22d3c1a027a1eec5d208585 usb: xhci: Add support for secondary interrupters
1007841 I1751352ebbe38d4b8c7886085d15043c2e5244f5 usb: phy: qusb-v2: Add USB QUSB PHY for newer platform
1036148 I1a7b77e3a64da245afd0c4f73c8d04627a061ada qcom-charger: qpnp-smb2: expose AICL settled current thr
1037821 I1a40bbe46e689d8c98f31cfef3a6743856288127 ARM: dts: msm: add Tri-color RGB LEDs on pmicobalt
1024187 1033071 Ia133b6c0cf0c21484f61631f04cba0d1112c9d48 leds: qpnp-flash-v2: Add operational current property
950797 I252f5171bd94b5ab096eb1a2f053f29a8c049c3b sound: usb: Add helper APIs to enable audio stream
1036622 I8c545248f23c73ff9fb470705f1c17175a8e4e0b ASoC: wcd9335: Fix traversing of source dapm widgets
925138 Ic1f02c341c06cadcfe6de638ff6c86e51845e59f ASoC: core: Fix possible NULL pointer de-reference
1038038 Ib01b649633ef8d7e15791fa9b1bd0d7a3fbffc5d ARM: dts: msm: remove VMBMS feature for msmcobalt
1024187 Ibdd139c98aa289f1d6b8545b953e79a1187284e9 leds: qpnp-flash-v2: Fix safety timer configuration
980027 I02036d79fca38ff9ac71c70de8aa144b9cfb2f59 icnss: Add support of enable HW debug from module parame
1024187 I3670eaa65efc9d1efb29fb30500fb9067885b446 ARM: dts: msm: Add current property for flash-led for pm
1024187 I640106abcf05949f2570efd42f925f1f73bdaa81 ARM: dts: msm: Add torch nodes to flash-led for pmicobal
862785 I6e3582fa010d18d4e0ccfde319dfc4d81af1351f ASoC: pcm: Update RX shutdown sequence
986695 I4a0b0ed4f6679da016b1b460cb597bc7fa2afa12 ASoC: msm: Dynamic allocation of loopback sessions
1027456 I6935c6de9009532570e1becad4ef1c1ee095d7c4 defconfig: Add initial 32-bit defconfig for msmfalcon
1034047 I48a767f7673defe60156c305e39f1ce4aa44306b msm: mdss: hdmi: fix deep color enable and audio clock
950797 Ie0756d646a396a11b41b93e886bca9aff636ee5d usb: xhci: Add helper APIs to return xhci dma addresses
1035315 1038542 Ie2ab84b02a8fb4070a0e86f09f52db9aa4163003 arm64: topology: Export arch_get_cpu_efficiency API
1018651 I20329a6834f5f1498388c39b1dd95db2896b3239 msm: camera: Return HAL buffer to fix drain issues
Change-Id: I46ec2b4cf40063eb9021f9150cb093d43abf5c8b
CRs-Fixed: 1038038, 1036148, 986695, 1027456, 1038542, 1007841, 925138, 950797, 862785, 1036622, 1033071, 1003482, 1018651, 1024187, 1037821, 1035315, 1034047, 980027
47 files changed, 2954 insertions, 237 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index 827f96c341b6..f1a8c77c8387 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -29,13 +29,15 @@ serve as an overall switch. not required for switch node. - qcom,max-current : Maximum current allowed on this LED. Valid values should be integer from 0 to 1500 inclusive. Flash 2 should have maximum current of - 750 per hardware requirement. Unit is mA. This is not required for switch - node. + 750 per hardware requirement. Unit is mA. For torch, the maximum current + is clamped at 500 mA. This is not required for the switch node. - qcom,duration-ms : Required property for flash nodes but not needed for torch. Integer type specifying flash duration. Values are from 10ms to 1280ms with 10ms resolution. This is not required for switch node. Optional properties inside child node: +- qcom,current-ma : operational current intensity for LED in mA. Accepted values are a + positive integer in the range of 0 to qcom,max-current inclusive. - qcom,ires-ua : Integer type to specify current resolution. Accepted values should be 12500, 10000, 7500, and 5000. Unit is uA. - qcom,hdrm-voltage-mv : Integer type specifying headroom voltage. Values are from 125mV to 500mV @@ -69,6 +71,7 @@ Example: qcom,default-led-trigger = "flash0_trigger"; qcom,id = <0>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -82,6 +85,7 @@ Example: qcom,default-led-trigger = "flash1_trigger"; qcom,id = <1>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -95,6 +99,7 @@ Example: qcom,default-led-trigger = "flash2_trigger"; qcom,id = <2>; + qcom,current-ma = <500>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -107,10 +112,11 @@ Example: pmi8998_torch0: qcom,torch_0 { label = "torch"; qcom,led-name = "led:torch_0"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch0_trigger"; qcom,id = <0>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; @@ -119,10 +125,11 @@ Example: pmi8998_torch1: qcom,torch_1 { label = "torch"; qcom,led-name = "led:torch_1"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch1_trigger"; qcom,id = <1>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; @@ -131,10 +138,11 @@ Example: pmi8998_torch2: qcom,torch_2 { label = "torch"; qcom,led-name = "led:torch_2"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch2_trigger"; qcom,id = <2>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index e909c67ca20a..35bb94b2ef70 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -143,7 +143,7 @@ Example: QUSB2 High-Speed PHY Required properties: - - compatible: Should be "qcom,qusb2phy" + - compatible: Should be "qcom,qusb2phy" or "qcom,qusb2phy-v2" - reg: Address and length of the QUSB2 PHY register set - reg-names: Should be "qusb_phy_base". - <supply-name>-supply: phandle to the regulator device tree node diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi index ab6b614471e5..6729899379c5 100644 --- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi +++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi @@ -51,7 +51,6 @@ voice: qcom,msm-pcm-voice { compatible = "qcom,msm-pcm-voice"; qcom,destroy-cvd; - qcom,vote-bms; }; stub_codec: qcom,msm-stub-codec { diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index b612ec3f42b5..f5b59e6de558 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -383,7 +383,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <2>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_4: pwm@b400 { @@ -396,7 +395,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <3>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_5: pwm@b500 { @@ -409,7 +407,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <4>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_6: pwm@b600 { @@ -425,6 +422,50 @@ status = "disabled"; }; + qcom,leds@d000 { + compatible = "qcom,leds-qpnp"; + reg = <0xd000 0x100>; + label = "rgb"; + status = "okay"; + + red_led: qcom,rgb_0 { + label = "rgb"; + qcom,id = <3>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_5 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "red"; + linux,default-trigger = + "battery-charging"; + }; + + green_led: qcom,rgb_1 { + label = "rgb"; + qcom,id = <4>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_4 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "green"; + linux,default-trigger = "battery-full"; + }; + + blue_led: qcom,rgb_2 { + label = "rgb"; + qcom,id = <5>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_3 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "blue"; + linux,default-trigger = "boot-indication"; + }; + }; + labibb: qpnp-labibb-regulator { compatible = "qcom,qpnp-labibb-regulator"; #address-cells = <1>; @@ -565,9 +606,9 @@ label = "flash"; qcom,led-name = "led:flash_0"; qcom,max-current = <1500>; - qcom,default-led-trigger = - "flash0_trigger"; + qcom,default-led-trigger = "flash0_trigger"; qcom,id = <0>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -578,9 +619,9 @@ label = "flash"; qcom,led-name = "led:flash_1"; qcom,max-current = <1500>; - qcom,default-led-trigger = - "flash1_trigger"; + qcom,default-led-trigger = "flash1_trigger"; qcom,id = <1>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -591,9 +632,9 @@ label = "flash"; qcom,led-name = "led:flash_2"; qcom,max-current = <750>; - qcom,default-led-trigger = - "flash2_trigger"; + qcom,default-led-trigger = "flash2_trigger"; qcom,id = <2>; + qcom,current-ma = <500>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -603,6 +644,45 @@ pinctrl-1 = <&led_disable>; }; + pmicobalt_torch0: qcom,torch_0 { + label = "torch"; + qcom,led-name = "led:torch_0"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch0_trigger"; + qcom,id = <0>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmicobalt_torch1: qcom,torch_1 { + label = "torch"; + qcom,led-name = "led:torch_1"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch1_trigger"; + qcom,id = <1>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmicobalt_torch2: qcom,torch_2 { + label = "torch"; + qcom,led-name = "led:torch_2"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch2_trigger"; + qcom,id = <2>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + pinctrl-names = "led_enable","led_disable"; + pinctrl-0 = <&led_enable>; + pinctrl-1 = <&led_disable>; + }; + pmicobalt_switch: qcom,led_switch { label = "switch"; qcom,led-name = "led:switch"; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig new file mode 100644 index 000000000000..5a74e7e69ae8 --- /dev/null +++ b/arch/arm/configs/msmcortex_defconfig @@ -0,0 +1,562 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_SHA512=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSMFALCON=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_ARM_PSCI=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_CLEANCACHE=y +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_SECCOMP=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_SCHED_FREQ_INPUT=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_DNS_RESOLVER=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +# CONFIG_CFG80211_CRDA_SUPPORT is not set +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=40 +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_UID_STAT=y +CONFIG_UID_CPUTIME=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_RNDIS_IPA=y +CONFIG_PHYLIB=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_ATH_CARDS=y +CONFIG_CLD_LL_CORE=y +CONFIG_QPNP_POWER_ON=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSMCOBALT=y +CONFIG_PINCTRL_MSMFALCON=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_QPNP_SMBCHARGER=y +CONFIG_SMB135X_CHARGER=y +CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_QPNP_SMB2=y +CONFIG_SMB138X_CHARGER=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_THERMAL=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_MSM_CDC_SUPPLY=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_CPRH_KBSS=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_QCOM_KGSL=y +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_SWITCH=y +CONFIG_EDAC=y +CONFIG_EDAC_MM_EDAC=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_USB_BAM=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_COMMON_LOG=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_DEBUG=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SPCOM=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_QCOM_DCC=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_ICNSS=y +CONFIG_MSM_GLADIATOR_ERP_V2=y +CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y +CONFIG_MSM_GLADIATOR_HANG_DETECT=y +CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_CPUSS_DUMP=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON=y +CONFIG_IIO=y +CONFIG_QCOM_RRADC=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_MSM_TZ_LOG=y +CONFIG_SENSORS_SSC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_SLUB_DEBUG_ON=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_UFS_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_QCOM_RTB=y +CONFIG_FUNCTION_TRACER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_MEMTEST=y +CONFIG_PANIC_ON_DATA_CORRUPTION=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_XZ_DEC=y diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index a3e9d6fdbf21..51b1c57c4443 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -21,6 +21,7 @@ extern struct cpu_topology cpu_topology[NR_CPUS]; void init_cpu_topology(void); void store_cpu_topology(unsigned int cpuid); const struct cpumask *cpu_coregroup_mask(int cpu); +unsigned long arch_get_cpu_efficiency(int cpu); #include <asm-generic/topology.h> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 67fc833a26af..349b131baec3 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -215,6 +215,7 @@ unsigned long arch_get_cpu_efficiency(int cpu) { return per_cpu(cpu_efficiency, cpu); } +EXPORT_SYMBOL(arch_get_cpu_efficiency); /* * Iterate all CPUs' descriptor in DT and compute the efficiency diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 89636557dec1..6942a8a74ea9 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -30,22 +30,20 @@ #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) -#define FLASH_LED_HDRM_MODE_PRGM_MASK 0xFF -#define FLASH_LED_HDRM_VOL_MASK 0xF0 -#define FLASH_LED_CURRENT_MASK 0x3F -#define FLASH_LED_STROBE_CTRL_MASK 0x07 -#define FLASH_LED_SAFETY_TMR_MASK_MASK 0x7F -#define FLASH_LED_MOD_CTRL_MASK 0x80 -#define FLASH_LED_ISC_DELAY_MASK 0x03 - -#define FLASH_LED_TYPE_FLASH 0 -#define FLASH_LED_TYPE_TORCH 1 +#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0) +#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) +#define FLASH_LED_CURRENT_MASK GENMASK(6, 0) +#define FLASH_LED_STROBE_CTRL_MASK GENMASK(2, 0) +#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0) +#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0) +#define FLASH_LED_MOD_CTRL_MASK BIT(7) + #define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true #define FLASH_LED_ISC_DELAY_SHIFT 6 #define FLASH_LED_ISC_DELAY_DEFAULT_US 3 #define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1 #define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 -#define FLASH_LED_SAFETY_TMR_ENABLED 0x08 +#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_IRES_BASE 3 #define FLASH_LED_IRES_DIVISOR 2500 #define FLASH_LED_IRES_MIN_UA 5000 @@ -56,10 +54,16 @@ #define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04 #define FLASH_LED_HDRM_VOL_BASE_MV 125 #define FLASH_LED_HDRM_VOL_STEP_MV 25 -#define FLASH_LED_STROBE_ENABLE 0x01 -#define FLASH_LED_MOD_ENABLE 0x80 +#define FLASH_LED_STROBE_ENABLE BIT(0) +#define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 +#define FLASH_LED_MIN_CURRENT_MA 25 + +enum flash_led_type { + FLASH_LED_TYPE_FLASH, + FLASH_LED_TYPE_TORCH, +}; /* * Flash LED configuration read from device tree @@ -141,13 +145,17 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) { - int prgm_current_ma; + int prgm_current_ma = value; + + if (value <= 0) + prgm_current_ma = 0; + else if (value < FLASH_LED_MIN_CURRENT_MA) + prgm_current_ma = FLASH_LED_MIN_CURRENT_MA; - prgm_current_ma = value < 0 ? 0 : value; - prgm_current_ma = value > fnode->cdev.max_brightness ? - fnode->cdev.max_brightness : value; + prgm_current_ma = min(prgm_current_ma, fnode->max_current); + fnode->current_ma = prgm_current_ma; fnode->cdev.brightness = prgm_current_ma; - fnode->brightness = prgm_current_ma * 1000 / fnode->ires_ua + 1; + fnode->current_reg_val = prgm_current_ma * 1000 / fnode->ires_ua + 1; fnode->led_on = prgm_current_ma != 0; } @@ -182,13 +190,13 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), - FLASH_LED_CURRENT_MASK, led->fnode[i].brightness); + FLASH_LED_CURRENT_MASK, led->fnode[i].current_reg_val); if (rc) return rc; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_SAFETY_TMR(led->base + addr_offset), - FLASH_LED_SAFETY_TMR_MASK_MASK, led->fnode[i].duration); + FLASH_LED_SAFETY_TMR_MASK, led->fnode[i].duration); if (rc) return rc; @@ -240,6 +248,7 @@ leds_turn_off: FLASH_LED_CURRENT_MASK, 0); if (rc) return rc; + led->fnode[i].led_on = false; if (led->fnode[i].pinctrl) { @@ -305,14 +314,6 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } - rc = of_property_read_u32(node, "qcom,max-current", &val); - if (!rc) { - fnode->cdev.max_brightness = val; - } else { - dev_err(&led->pdev->dev, "Unable to read max current\n"); - return rc; - } - rc = of_property_read_string(node, "label", &temp_string); if (!rc) { if (!strcmp(temp_string, "flash")) @@ -355,13 +356,43 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } + rc = of_property_read_u32(node, "qcom,max-current", &val); + if (!rc) { + if (val < FLASH_LED_MIN_CURRENT_MA) + val = FLASH_LED_MIN_CURRENT_MA; + fnode->max_current = val; + fnode->cdev.max_brightness = val; + } else { + dev_err(&led->pdev->dev, + "Unable to read max current, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,current-ma", &val); + if (!rc) { + if (val < FLASH_LED_MIN_CURRENT_MA || + val > fnode->max_current) + dev_warn(&led->pdev->dev, + "Invalid operational current specified, capping it\n"); + if (val < FLASH_LED_MIN_CURRENT_MA) + val = FLASH_LED_MIN_CURRENT_MA; + if (val > fnode->max_current) + val = fnode->max_current; + fnode->current_ma = val; + fnode->cdev.brightness = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read operational current, rc=%d\n", rc); + return rc; + } + fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED; rc = of_property_read_u32(node, "qcom,duration-ms", &val); if (!rc) { fnode->duration = (u8)(((val - FLASH_LED_SAFETY_TMR_VAL_OFFSET) / FLASH_LED_SAFETY_TMR_VAL_DIVISOR) | - FLASH_LED_SAFETY_TMR_ENABLED); + FLASH_LED_SAFETY_TMR_ENABLE); } else if (rc == -EINVAL) { if (fnode->type == FLASH_LED_TYPE_FLASH) { dev_err(&led->pdev->dev, diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 60a834588767..c1aeb8c43e81 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -475,6 +475,38 @@ static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh, return rc; } +static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct camera_v4l2_private *sp = fh_to_private(fh); + struct msm_video_device *pvdev = video_drvdata(filep); + struct msm_camera_private_ioctl_arg *k_ioctl = arg; + long rc = -EINVAL; + + if (WARN_ON(!k_ioctl || !pvdev)) + return -EIO; + + switch (k_ioctl->id) { + case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { + struct msm_camera_return_buf ptr, *tmp = NULL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr, + sizeof(tmp)); + if (copy_from_user(&ptr, tmp, + sizeof(struct msm_camera_return_buf))) { + return -EFAULT; + } + rc = msm_vb2_return_buf_by_idx(pvdev->vdev->num, sp->stream_id, + ptr.index); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl->id); + return -EINVAL; + } + return rc; +} + static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = { .vidioc_querycap = camera_v4l2_querycap, .vidioc_s_crop = camera_v4l2_s_crop, @@ -499,6 +531,7 @@ static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = { /* event subscribe/unsubscribe */ .vidioc_subscribe_event = camera_v4l2_subscribe_event, .vidioc_unsubscribe_event = camera_v4l2_unsubscribe_event, + .vidioc_default = camera_v4l2_vidioc_private_ioctl, }; static int camera_v4l2_fh_open(struct file *filep) @@ -747,10 +780,62 @@ static int camera_v4l2_close(struct file *filep) } #ifdef CONFIG_COMPAT +static long camera_handle_internal_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long rc = 0; + struct msm_camera_private_ioctl_arg k_ioctl; + void __user *tmp_compat_ioctl_ptr = NULL; + + rc = msm_copy_camera_private_ioctl_args(arg, + &k_ioctl, &tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Subdev cmd %d failed\n", cmd); + return rc; + } + switch (k_ioctl.id) { + case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { + if (k_ioctl.size != sizeof(struct msm_camera_return_buf)) { + pr_debug("Invalid size for id %d with size %d", + k_ioctl.id, k_ioctl.size); + return -EINVAL; + } + k_ioctl.ioctl_ptr = (__u64)tmp_compat_ioctl_ptr; + if (!k_ioctl.ioctl_ptr) { + pr_debug("Invalid ptr for id %d", k_ioctl.id); + return -EINVAL; + } + rc = camera_v4l2_vidioc_private_ioctl(file, file->private_data, + 0, cmd, (void *)&k_ioctl); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } + return rc; +} + long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return -ENOIOCTLCMD; + long ret = 0; + + switch (cmd) { + case VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD: { + ret = camera_handle_internal_compat_ioctl(file, cmd, arg); + if (ret < 0) { + pr_debug("Subdev cmd %d fail\n", cmd); + return ret; + } + } + break; + default: + ret = -ENOIOCTLCMD; + break; + + } + return ret; } #endif static struct v4l2_file_operations camera_v4l2_fops = { diff --git a/drivers/media/platform/msm/camera_v2/fd/Makefile b/drivers/media/platform/msm/camera_v2/fd/Makefile index 82b37a73bfa3..8d01d3a8708d 100644 --- a/drivers/media/platform/msm/camera_v2/fd/Makefile +++ b/drivers/media/platform/msm/camera_v2/fd/Makefile @@ -1,5 +1,8 @@ GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc) ccflags-y += -Idrivers/media/video/msm ccflags-y += -Idrivers/media/platform/msm/camera_v2/common +ccflags-y += -Idrivers/media/platform/msm/camera_v2 +ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc/cpp +ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/ obj-$(CONFIG_MSM_FD) += msm_fd_dev.o msm_fd_hw.o diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index a34dbb80b468..c6dc4b75e479 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1099,6 +1099,28 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q) } EXPORT_SYMBOL(msm_get_stream_from_vb2q); +#ifdef CONFIG_COMPAT +long msm_copy_camera_private_ioctl_args(unsigned long arg, + struct msm_camera_private_ioctl_arg *k_ioctl, + void __user **tmp_compat_ioctl_ptr) +{ + struct msm_camera_private_ioctl_arg *up_ioctl_ptr = + (struct msm_camera_private_ioctl_arg *)arg; + + if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr)) + return -EIO; + + k_ioctl->id = up_ioctl_ptr->id; + k_ioctl->size = up_ioctl_ptr->size; + k_ioctl->result = up_ioctl_ptr->result; + k_ioctl->reserved = up_ioctl_ptr->reserved; + *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr); + + return 0; +} +EXPORT_SYMBOL(msm_copy_camera_private_ioctl_args); +#endif + static void msm_sd_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h index cab07df2a5bb..2b3576b8edd2 100644 --- a/drivers/media/platform/msm/camera_v2/msm.h +++ b/drivers/media/platform/msm/camera_v2/msm.h @@ -133,4 +133,9 @@ struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id, unsigned int stream_id); struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q); struct msm_session *msm_session_find(unsigned int session_id); +#ifdef CONFIG_COMPAT +long msm_copy_camera_private_ioctl_args(unsigned long arg, + struct msm_camera_private_ioctl_arg *k_ioctl, + void __user **tmp_compat_ioctl_ptr); +#endif #endif /*_MSM_H */ diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index b6cbdcc32360..ac9a4b2048d1 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -48,6 +48,7 @@ static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev, } return 0; } + static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev, void __user *argp) { @@ -91,6 +92,52 @@ static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev, return rc; } +static int32_t msm_buf_mngr_get_buf_by_idx(struct msm_buf_mngr_device *dev, + void *argp) +{ + unsigned long flags; + int32_t rc = 0; + struct msm_buf_mngr_info *buf_info = + (struct msm_buf_mngr_info *)argp; + struct msm_get_bufs *new_entry = + kzalloc(sizeof(struct msm_get_bufs), GFP_KERNEL); + + if (!new_entry) + return -ENOMEM; + + if (!buf_info) { + kfree(new_entry); + return -EIO; + } + + INIT_LIST_HEAD(&new_entry->entry); + new_entry->vb2_v4l2_buf = dev->vb2_ops.get_buf_by_idx( + buf_info->session_id, buf_info->stream_id, buf_info->index); + if (!new_entry->vb2_v4l2_buf) { + pr_debug("%s:Get buf is null\n", __func__); + kfree(new_entry); + return -EINVAL; + } + new_entry->session_id = buf_info->session_id; + new_entry->stream_id = buf_info->stream_id; + new_entry->index = new_entry->vb2_v4l2_buf->vb2_buf.index; + spin_lock_irqsave(&dev->buf_q_spinlock, flags); + list_add_tail(&new_entry->entry, &dev->buf_qhead); + spin_unlock_irqrestore(&dev->buf_q_spinlock, flags); + if (buf_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) { + mutex_lock(&dev->cont_mutex); + if (!list_empty(&dev->cont_qhead)) { + rc = msm_buf_mngr_hdl_cont_get_buf(dev, buf_info); + } else { + pr_err("Nothing mapped in user buf for %d,%d\n", + buf_info->session_id, buf_info->stream_id); + rc = -EINVAL; + } + mutex_unlock(&dev->cont_mutex); + } + return rc; +} + static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, struct msm_buf_mngr_info *buf_info) { @@ -413,6 +460,67 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, return rc; } +int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp) +{ + int rc = 0; + + if (!msm_buf_mngr_dev) + return -ENODEV; + if (!argp) + return -EINVAL; + + switch (cmd) { + case VIDIOC_MSM_BUF_MNGR_GET_BUF: + rc = msm_buf_mngr_get_buf(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_BUF_DONE: + rc = msm_buf_mngr_buf_done(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_PUT_BUF: + rc = msm_buf_mngr_put_buf(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + struct msm_camera_private_ioctl_arg *k_ioctl = argp; + + switch (k_ioctl->id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info *tmp = NULL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + if (k_ioctl->size != sizeof(struct msm_buf_mngr_info)) + return -EINVAL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr, + sizeof(tmp)); + rc = msm_buf_mngr_get_buf_by_idx(msm_buf_mngr_dev, + tmp); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl->id); + return -EINVAL; + } + break; + } + default: + return -ENOIOCTLCMD; + } + + return rc; +} + +int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct) +{ + if (!msm_buf_mngr_dev) + return -ENODEV; + if (!cb_struct) + return -EINVAL; + + cb_struct->msm_cam_buf_mgr_ops = msm_cam_buf_mgr_ops; + return 0; +} + static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { @@ -425,16 +533,45 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, rc = -ENOMEM; return rc; } - switch (cmd) { - case VIDIOC_MSM_BUF_MNGR_GET_BUF: - rc = msm_buf_mngr_get_buf(buf_mngr_dev, argp); + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + struct msm_camera_private_ioctl_arg k_ioctl, *ptr; + + if (!arg) + return -EINVAL; + ptr = arg; + k_ioctl = *ptr; + switch (k_ioctl.id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info buf_info, *tmp = NULL; + + if (k_ioctl.size != sizeof(struct msm_buf_mngr_info)) + return -EINVAL; + if (!k_ioctl.ioctl_ptr) + return -EINVAL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl.ioctl_ptr, + sizeof(tmp)); + if (copy_from_user(&buf_info, tmp, + sizeof(struct msm_buf_mngr_info))) { + return -EFAULT; + } + MSM_CAM_GET_IOCTL_ARG_PTR(&k_ioctl.ioctl_ptr, + &buf_info, sizeof(void *)); + argp = &k_ioctl; + rc = msm_cam_buf_mgr_ops(cmd, argp); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } break; + } + case VIDIOC_MSM_BUF_MNGR_GET_BUF: case VIDIOC_MSM_BUF_MNGR_BUF_DONE: - rc = msm_buf_mngr_buf_done(buf_mngr_dev, argp); - break; case VIDIOC_MSM_BUF_MNGR_PUT_BUF: - rc = msm_buf_mngr_put_buf(buf_mngr_dev, argp); + rc = msm_cam_buf_mgr_ops(cmd, argp); break; case VIDIOC_MSM_BUF_MNGR_INIT: rc = msm_generic_buf_mngr_open(sd, NULL); @@ -462,6 +599,107 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, } #ifdef CONFIG_COMPAT +static long msm_camera_buf_mgr_fetch_buf_info( + struct msm_buf_mngr_info32_t *buf_info32, + struct msm_buf_mngr_info *buf_info, unsigned long arg) +{ + if (!arg || !buf_info32 || !buf_info) + return -EINVAL; + + if (copy_from_user(buf_info32, (void __user *)arg, + sizeof(struct msm_buf_mngr_info32_t))) + return -EFAULT; + + buf_info->session_id = buf_info32->session_id; + buf_info->stream_id = buf_info32->stream_id; + buf_info->frame_id = buf_info32->frame_id; + buf_info->index = buf_info32->index; + buf_info->timestamp.tv_sec = (long) buf_info32->timestamp.tv_sec; + buf_info->timestamp.tv_usec = (long) buf_info32-> + timestamp.tv_usec; + buf_info->reserved = buf_info32->reserved; + buf_info->type = buf_info32->type; + return 0; +} + +static long msm_camera_buf_mgr_update_buf_info( + struct msm_buf_mngr_info32_t *buf_info32, + struct msm_buf_mngr_info *buf_info, unsigned long arg) +{ + if (!arg || !buf_info32 || !buf_info) + return -EINVAL; + + buf_info32->session_id = buf_info->session_id; + buf_info32->stream_id = buf_info->stream_id; + buf_info32->index = buf_info->index; + buf_info32->timestamp.tv_sec = (int32_t) buf_info-> + timestamp.tv_sec; + buf_info32->timestamp.tv_usec = (int32_t) buf_info->timestamp. + tv_usec; + buf_info32->reserved = buf_info->reserved; + buf_info32->type = buf_info->type; + buf_info32->user_buf.buf_cnt = buf_info->user_buf.buf_cnt; + memcpy(&buf_info32->user_buf.buf_idx, + &buf_info->user_buf.buf_idx, + sizeof(buf_info->user_buf.buf_idx)); + if (copy_to_user((void __user *)arg, buf_info32, + sizeof(struct msm_buf_mngr_info32_t))) + return -EFAULT; + return 0; +} +static long msm_camera_buf_mgr_internal_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + long rc = 0; + struct msm_camera_private_ioctl_arg k_ioctl; + void __user *tmp_compat_ioctl_ptr = NULL; + + rc = msm_copy_camera_private_ioctl_args(arg, + &k_ioctl, &tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Subdev cmd %d failed\n", cmd); + return rc; + } + + switch (k_ioctl.id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info32_t buf_info32; + struct msm_buf_mngr_info buf_info; + + if (k_ioctl.size != sizeof(struct msm_buf_mngr_info32_t)) { + pr_err("Invalid size for id %d with size %d", + k_ioctl.id, k_ioctl.size); + return -EINVAL; + } + if (!tmp_compat_ioctl_ptr) { + pr_err("Invalid ptr for id %d", k_ioctl.id); + return -EINVAL; + } + k_ioctl.ioctl_ptr = (__u64)&buf_info; + rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info, + (unsigned long)tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Fetch buf info failed for cmd=%d", cmd); + return rc; + } + rc = v4l2_subdev_call(sd, core, ioctl, cmd, &k_ioctl); + if (rc < 0) { + pr_err("Subdev cmd %d failed for id %d", cmd, + k_ioctl.id); + return rc; + } + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } + + return 0; +} + static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -469,8 +707,6 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); int32_t rc = 0; - void __user *up = (void __user *)arg; - /* Convert 32 bit IOCTL ID's to 64 bit IOCTL ID's * except VIDIOC_MSM_CPP_CFG32, which needs special * processing @@ -486,13 +722,14 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, cmd = VIDIOC_MSM_BUF_MNGR_PUT_BUF; break; case VIDIOC_MSM_BUF_MNGR_CONT_CMD: - cmd = VIDIOC_MSM_BUF_MNGR_CONT_CMD; break; case VIDIOC_MSM_BUF_MNGR_FLUSH32: cmd = VIDIOC_MSM_BUF_MNGR_FLUSH; break; + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: + break; default: - pr_debug("%s : unsupported compat type", __func__); + pr_debug("unsupported compat type\n"); return -ENOIOCTLCMD; } @@ -504,65 +741,51 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, struct msm_buf_mngr_info32_t buf_info32; struct msm_buf_mngr_info buf_info; - if (copy_from_user(&buf_info32, (void __user *)up, - sizeof(struct msm_buf_mngr_info32_t))) - return -EFAULT; - - buf_info.session_id = buf_info32.session_id; - buf_info.stream_id = buf_info32.stream_id; - buf_info.frame_id = buf_info32.frame_id; - buf_info.index = buf_info32.index; - buf_info.timestamp.tv_sec = (long) buf_info32.timestamp.tv_sec; - buf_info.timestamp.tv_usec = (long) buf_info32. - timestamp.tv_usec; - buf_info.reserved = buf_info32.reserved; - buf_info.type = buf_info32.type; - + rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info, + arg); + if (rc < 0) { + pr_err("Fetch buf info failed for cmd=%d\n", cmd); + return rc; + } rc = v4l2_subdev_call(sd, core, ioctl, cmd, &buf_info); if (rc < 0) { - pr_debug("%s : Subdev cmd %d fail", __func__, cmd); + pr_debug("Subdev cmd %d fail\n", cmd); + return rc; + } + rc = msm_camera_buf_mgr_update_buf_info(&buf_info32, &buf_info, + arg); + if (rc < 0) { + pr_err("Update buf info failed for cmd=%d\n", cmd); + return rc; + } + break; + } + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + rc = msm_camera_buf_mgr_internal_compat_ioctl(file, cmd, arg); + if (rc < 0) { + pr_debug("Subdev cmd %d fail\n", cmd); return rc; } - - buf_info32.session_id = buf_info.session_id; - buf_info32.stream_id = buf_info.stream_id; - buf_info32.index = buf_info.index; - buf_info32.timestamp.tv_sec = (int32_t) buf_info. - timestamp.tv_sec; - buf_info32.timestamp.tv_usec = (int32_t) buf_info.timestamp. - tv_usec; - buf_info32.reserved = buf_info.reserved; - buf_info32.type = buf_info.type; - buf_info32.user_buf.buf_cnt = buf_info.user_buf.buf_cnt; - memcpy(&buf_info32.user_buf.buf_idx, - &buf_info.user_buf.buf_idx, - sizeof(buf_info.user_buf.buf_idx)); - if (copy_to_user((void __user *)up, &buf_info32, - sizeof(struct msm_buf_mngr_info32_t))) - return -EFAULT; } break; case VIDIOC_MSM_BUF_MNGR_CONT_CMD: { struct msm_buf_mngr_main_cont_info cont_cmd; - if (copy_from_user(&cont_cmd, (void __user *)up, + if (copy_from_user(&cont_cmd, (void __user *)arg, sizeof(struct msm_buf_mngr_main_cont_info))) return -EFAULT; rc = v4l2_subdev_call(sd, core, ioctl, cmd, &cont_cmd); if (rc < 0) { - pr_debug("%s : Subdev cmd %d fail", __func__, cmd); + pr_debug("Subdev cmd %d fail\n", cmd); return rc; } } break; default: - pr_debug("%s : unsupported compat type", __func__); + pr_debug("unsupported compat type\n"); return -ENOIOCTLCMD; break; } - - - return 0; } #endif diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index dc7f745f6190..c2f1a1527800 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -52,4 +52,14 @@ struct msm_buf_mngr_user_buf_cont_info { uint32_t cnt; struct ion_handle *ion_handle; }; + +/* kernel space functions*/ +struct msm_cam_buf_mgr_req_ops { + int (*msm_cam_buf_mgr_ops)(unsigned int cmd, void *argp); +}; + +/* API to register callback from client. This assumes cb_struct is allocated by + * client. + */ +int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct); #endif diff --git a/drivers/media/platform/msm/camera_v2/msm_sd.h b/drivers/media/platform/msm/camera_v2/msm_sd.h index 6bb95ad0de87..d893d9fc07e3 100644 --- a/drivers/media/platform/msm/camera_v2/msm_sd.h +++ b/drivers/media/platform/msm/camera_v2/msm_sd.h @@ -73,6 +73,8 @@ struct msm_sd_req_vb2_q { unsigned int stream_id); struct vb2_queue * (*get_vb2_queue)(int session_id, unsigned int stream_id); + struct vb2_v4l2_buffer * (*get_buf_by_idx)(int session_id, + unsigned int stream_id, uint32_t index); int (*put_buf)(struct vb2_v4l2_buffer *vb2_buf, int session_id, unsigned int stream_id); int (*buf_done)(struct vb2_v4l2_buffer *vb2_v4l2_buf, int session_id, @@ -85,6 +87,9 @@ struct msm_sd_req_vb2_q { #define MSM_SD_NOTIFY_PUT_SD 0x00000002 #define MSM_SD_NOTIFY_REQ_CB 0x00000003 +#define MSM_CAM_GET_IOCTL_ARG_PTR(ptr, \ + ioctl_ptr, len) memcpy(ptr, ioctl_ptr, len) + int msm_sd_register(struct msm_sd_subdev *msm_subdev); int msm_sd_unregister(struct msm_sd_subdev *sd); struct v4l2_subdev *msm_sd_get_subdev(struct v4l2_subdev *sd, diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c index 6e0dac4608a9..59135225cb15 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c @@ -231,6 +231,41 @@ end: return vb2_v4l2_buf; } +static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, + unsigned int stream_id, uint32_t index) +{ + struct msm_stream *stream; + struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_vb2_buffer *msm_vb2 = NULL; + unsigned long flags; + + stream = msm_get_stream(session_id, stream_id); + if (IS_ERR_OR_NULL(stream)) + return NULL; + + spin_lock_irqsave(&stream->stream_lock, flags); + + if (!stream->vb2_q) { + pr_err("%s: stream q not available\n", __func__); + goto end; + } + + list_for_each_entry(msm_vb2, &(stream->queued_list), list) { + vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); + if ((vb2_v4l2_buf->vb2_buf.index != index) || msm_vb2->in_freeq + || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) + continue; + + msm_vb2->in_freeq = 1; + goto end; + } + msm_vb2 = NULL; + vb2_v4l2_buf = NULL; +end: + spin_unlock_irqrestore(&stream->stream_lock, flags); + return vb2_v4l2_buf; +} + static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, unsigned int stream_id) { @@ -320,6 +355,48 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, return rc; } +long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, + uint32_t index) +{ + struct msm_stream *stream; + struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_vb2_buffer *msm_vb2 = NULL; + unsigned long flags; + long rc = -EINVAL; + + stream = msm_get_stream(session_id, stream_id); + if (IS_ERR_OR_NULL(stream)) + return rc; + + spin_lock_irqsave(&stream->stream_lock, flags); + + if (!stream->vb2_q) { + pr_err("%s: stream q not available\n", __func__); + goto end; + } + + list_for_each_entry(msm_vb2, &(stream->queued_list), list) { + vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); + if ((vb2_v4l2_buf->vb2_buf.index != index) + || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) + continue; + + if (!msm_vb2->in_freeq) { + vb2_buffer_done(&vb2_v4l2_buf->vb2_buf, + VB2_BUF_STATE_ERROR); + rc = 0; + } else { + rc = -EINVAL; + } + break; + } + +end: + spin_unlock_irqrestore(&stream->stream_lock, flags); + return rc; +} +EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); + static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) { unsigned long flags; @@ -350,11 +427,11 @@ int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req) } req->get_buf = msm_vb2_get_buf; + req->get_buf_by_idx = msm_vb2_get_buf_by_idx; req->get_vb2_queue = msm_vb2_get_queue; req->put_buf = msm_vb2_put_buf; req->buf_done = msm_vb2_buf_done; req->flush_buf = msm_vb2_flush_buf; - return 0; } diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h index 96fa1d4c64c9..53511d5416d7 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* 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 @@ -65,5 +65,7 @@ struct msm_stream { struct vb2_ops *msm_vb2_get_q_ops(void); struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void); int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd); +long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, + uint32_t index); #endif /*_MSM_VB_H */ diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c index 8024fab80295..1b93c83ae98e 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/drivers/mfd/wcd9xxx-irq.c @@ -390,18 +390,21 @@ void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); } void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); } void wcd9xxx_disable_irq_sync( struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); } static int wcd9xxx_irq_setup_downstream_irq( @@ -551,6 +554,7 @@ void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) disable_irq_wake(wcd9xxx_res->irq); free_irq(wcd9xxx_res->irq, wcd9xxx_res); /* Release parent's of node */ + wcd9xxx_res->irq = 0; wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); } mutex_destroy(&wcd9xxx_res->irq_lock); diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index dbdcd9026541..3c934cb0b1f9 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -145,6 +145,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -201,6 +202,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_ACTIVE: val->intval = chg->pd_active; break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + rc = smblib_get_prop_input_current_settled(chg, val); + break; default: pr_err("get prop %d is not supported\n", psp); rc = -EINVAL; diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 46b824376847..0465bfac296d 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -1066,6 +1066,12 @@ int smblib_get_prop_pd_allowed(struct smb_charger *chg, return 0; } +int smblib_get_prop_input_current_settled(struct smb_charger *chg, + union power_supply_propval *val) +{ + return smblib_get_charge_param(chg, &chg->param.icl_stat, &val->intval); +} + /******************* * USB PSY SETTERS * * *****************/ diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index fbadf02d9958..fe75e0625230 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -182,6 +182,8 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_pd_allowed(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_input_current_settled(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_usb_current_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index f4b63ec4b325..5c5bd1975fb3 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -97,6 +97,16 @@ } while (0) #endif +enum icnss_debug_quirks { + HW_ALWAY_ON, + HW_DEBUG_ENABLE, +}; + +#define ICNSS_QUIRKS_DEFAULT 0 + +unsigned long quirks = ICNSS_QUIRKS_DEFAULT; +module_param(quirks, ulong, 0600); + void *icnss_ipc_log_context; enum icnss_driver_event_type { @@ -783,6 +793,8 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) memset(&resp, 0, sizeof(resp)); req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks); req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01; diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.c b/drivers/soc/qcom/wlan_firmware_service_v01.c index e899459a5a40..b33575116c18 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.c +++ b/drivers/soc/qcom/wlan_firmware_service_v01.c @@ -462,6 +462,24 @@ struct elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { mode), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = QMI_COMMON_TLV_TYPE, diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.h b/drivers/soc/qcom/wlan_firmware_service_v01.h index 6e96cbabd9d8..bf47da7cdecb 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.h +++ b/drivers/soc/qcom/wlan_firmware_service_v01.h @@ -180,8 +180,10 @@ extern struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; struct wlfw_wlan_mode_req_msg_v01 { enum wlfw_driver_mode_enum_v01 mode; + uint8_t hw_debug_valid; + uint8_t hw_debug; }; -#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 7 +#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11 extern struct elem_info wlfw_wlan_mode_req_msg_v01_ei[]; struct wlfw_wlan_mode_resp_msg_v01 { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9b2b98cf1823..5cc655908fda 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2202,8 +2202,65 @@ int usb_hcd_get_frame_number (struct usb_device *udev) return hcd->driver->get_frame_number (hcd); } +int usb_hcd_sec_event_ring_setup(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->sec_event_ring_setup(hcd, intr_num); +} + +int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->sec_event_ring_cleanup(hcd, intr_num); +} + /*-------------------------------------------------------------------------*/ +dma_addr_t +usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_sec_event_ring_dma_addr(hcd, intr_num); +} + +dma_addr_t +usb_hcd_get_dcba_dma_addr(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_dcba_dma_addr(hcd, udev); +} + +dma_addr_t +usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_xfer_ring_dma_addr(hcd, udev, ep); +} + #ifdef CONFIG_PM int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f8bbd0b6d9fe..b43d542e3bd4 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -669,6 +669,57 @@ int usb_get_current_frame_number(struct usb_device *dev) } EXPORT_SYMBOL_GPL(usb_get_current_frame_number); +int usb_sec_event_ring_setup(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_sec_event_ring_setup(dev, intr_num); +} +EXPORT_SYMBOL(usb_sec_event_ring_setup); + +int usb_sec_event_ring_cleanup(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_sec_event_ring_cleanup(dev, intr_num); +} +EXPORT_SYMBOL(usb_sec_event_ring_cleanup); + +dma_addr_t +usb_get_sec_event_ring_dma_addr(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_sec_event_ring_dma_addr(dev, intr_num); +} +EXPORT_SYMBOL(usb_get_sec_event_ring_dma_addr); + +dma_addr_t +usb_get_dcba_dma_addr(struct usb_device *dev) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_dcba_dma_addr(dev); +} +EXPORT_SYMBOL(usb_get_dcba_dma_addr); + +dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev, + struct usb_host_endpoint *ep) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_xfer_ring_dma_addr(dev, ep); +} +EXPORT_SYMBOL(usb_get_xfer_ring_dma_addr); + /*-------------------------------------------------------------------*/ /* * __usb_get_extra_descriptor() finds a descriptor of specific type in the diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c48cbe731356..2ac142e3cce9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1786,25 +1786,73 @@ void xhci_free_command(struct xhci_hcd *xhci, kfree(command); } -void xhci_mem_cleanup(struct xhci_hcd *xhci) +int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num) { + int size; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct device *dev = xhci_to_hcd(xhci)->self.controller; + + if (intr_num > xhci->max_interrupters) { + xhci_err(xhci, "invalid secondary interrupter num %d\n", + intr_num); + return -EINVAL; + } + + size = + sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries); + if (xhci->sec_erst[intr_num].entries) + dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries, + xhci->sec_erst[intr_num].erst_dma_addr); + xhci->sec_erst[intr_num].entries = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d", + intr_num); + if (xhci->sec_event_ring[intr_num]) + xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]); + xhci->sec_event_ring[intr_num] = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Freed sec event ring"); + + return 0; +} + +void xhci_event_ring_cleanup(struct xhci_hcd *xhci) +{ int size; - int i, j, num_ports; + unsigned int i; + struct device *dev = xhci_to_hcd(xhci)->self.controller; - del_timer_sync(&xhci->cmd_timer); + /* sec event ring clean up */ + for (i = 1; i <= xhci->max_interrupters; i++) + xhci_sec_event_ring_cleanup(xhci_to_hcd(xhci), i); - /* Free the Event Ring Segment Table and the actual Event Ring */ + kfree(xhci->sec_ir_set); + xhci->sec_ir_set = NULL; + kfree(xhci->sec_erst); + xhci->sec_erst = NULL; + kfree(xhci->sec_event_ring); + xhci->sec_event_ring = NULL; + + /* primary event ring clean up */ size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) dma_free_coherent(dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary ERST"); if (xhci->event_ring) xhci_ring_free(xhci, xhci->event_ring); xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed priamry event ring"); +} + +void xhci_mem_cleanup(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + int i, j, num_ports; + + del_timer_sync(&xhci->cmd_timer); + + xhci_event_ring_cleanup(xhci); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -2039,30 +2087,6 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) return 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) -{ - u64 temp; - dma_addr_t deq; - - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) - xhci_warn(xhci, "WARN something wrong with SW event ring " - "dequeue ptr.\n"); - /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; - /* Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - temp &= ~ERST_EHB; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write event ring dequeue pointer, " - "preserving EHB bit"); - xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); -} - static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, __le32 __iomem *addr, u8 major_revision, int max_caps) { @@ -2340,13 +2364,184 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +int xhci_event_ring_setup(struct xhci_hcd *xhci, struct xhci_ring **er, + struct xhci_intr_reg __iomem *ir_set, struct xhci_erst *erst, + unsigned int intr_num, gfp_t flags) +{ + dma_addr_t dma, deq; + u64 val_64; + unsigned int val; + struct xhci_segment *seg; + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + *er = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, + TYPE_EVENT, flags); + if (!*er) + return -ENOMEM; + + erst->entries = dma_alloc_coherent(dev, + sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, + flags); + if (!erst->entries) { + xhci_ring_free(xhci, *er); + return -ENOMEM; + } + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: Allocated event ring segment table at 0x%llx", + intr_num, (unsigned long long)dma); + + memset(erst->entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); + erst->num_entries = ERST_NUM_SEGS; + erst->erst_dma_addr = dma; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: num segs = %i, virt addr = %p, dma addr = 0x%llx", + intr_num, + erst->num_entries, + erst->entries, + (unsigned long long)erst->erst_dma_addr); + + /* set ring base address and size for each segment table entry */ + for (val = 0, seg = (*er)->first_seg; val < ERST_NUM_SEGS; val++) { + struct xhci_erst_entry *entry = &erst->entries[val]; + + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + + /* set ERST count with the number of entries in the segment table */ + val = readl_relaxed(&ir_set->erst_size); + val &= ERST_SIZE_MASK; + val |= ERST_NUM_SEGS; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Write ERST size = %i to ir_set %d (some bits preserved)", val, + intr_num); + writel_relaxed(val, &ir_set->erst_size); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: Set ERST entries to point to event ring.", + intr_num); + /* set the segment table base address */ + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Set ERST base address for ir_set %d = 0x%llx", + intr_num, + (unsigned long long)erst->erst_dma_addr); + val_64 = xhci_read_64(xhci, &ir_set->erst_base); + val_64 &= ERST_PTR_MASK; + val_64 |= (erst->erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, val_64, &ir_set->erst_base); + + /* Set the event ring dequeue address */ + deq = xhci_trb_virt_to_dma((*er)->deq_seg, (*er)->dequeue); + if (deq == 0 && !in_interrupt()) + xhci_warn(xhci, + "intr# %d:WARN something wrong with SW event ring deq ptr.\n", + intr_num); + /* Update HC event ring dequeue pointer */ + val_64 = xhci_read_64(xhci, &ir_set->erst_dequeue); + val_64 &= ERST_PTR_MASK; + /* Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + val_64 &= ~ERST_EHB; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d:Write event ring dequeue pointer, preserving EHB bit", + intr_num); + xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | val_64, + &ir_set->erst_dequeue); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Wrote ERST address to ir_set %d.", intr_num); + xhci_print_ir_set(xhci, intr_num); + + return 0; +} + +int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned intr_num) +{ + int ret; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if ((xhci->xhc_state & XHCI_STATE_HALTED) || !xhci->sec_ir_set + || !xhci->sec_event_ring || !xhci->sec_erst || + intr_num > xhci->max_interrupters) { + xhci_err(xhci, + "%s:state %x ir_set %p evt_ring %p erst %p intr# %d\n", + __func__, xhci->xhc_state, xhci->sec_ir_set, + xhci->sec_event_ring, xhci->sec_erst, intr_num); + return -EINVAL; + } + + if (xhci->sec_event_ring && xhci->sec_event_ring[intr_num] + && xhci->sec_event_ring[intr_num]->first_seg) + goto done; + + xhci->sec_ir_set[intr_num] = &xhci->run_regs->ir_set[intr_num]; + ret = xhci_event_ring_setup(xhci, + &xhci->sec_event_ring[intr_num], + xhci->sec_ir_set[intr_num], + &xhci->sec_erst[intr_num], + intr_num, GFP_KERNEL); + if (ret) { + xhci_err(xhci, "sec event ring setup failed inter#%d\n", + intr_num); + return ret; + } +done: + return 0; +} + +int xhci_event_ring_init(struct xhci_hcd *xhci, gfp_t flags) +{ + int ret = 0; + + /* primary + secondary */ + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Allocating primary event ring"); + + /* Set ir_set to interrupt register set 0 */ + xhci->ir_set = &xhci->run_regs->ir_set[0]; + ret = xhci_event_ring_setup(xhci, &xhci->event_ring, xhci->ir_set, + &xhci->erst, 0, flags); + if (ret) { + xhci_err(xhci, "failed to setup primary event ring\n"); + goto fail; + } + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Allocating sec event ring related pointers"); + + xhci->sec_ir_set = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_ir_set), flags); + if (!xhci->sec_ir_set) { + ret = -ENOMEM; + goto fail; + } + + xhci->sec_event_ring = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_event_ring), flags); + if (!xhci->sec_event_ring) { + ret = -ENOMEM; + goto fail; + } + + xhci->sec_erst = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_erst), flags); + if (!xhci->sec_erst) + ret = -ENOMEM; +fail: + return ret; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; struct device *dev = xhci_to_hcd(xhci)->self.controller; unsigned int val, val2; u64 val_64; - struct xhci_segment *seg; u32 page_size, temp; int i; @@ -2472,73 +2667,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->dba = (void __iomem *) xhci->cap_regs + val; xhci_dbg_regs(xhci); xhci_print_run_regs(xhci); - /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; /* * Event ring setup: Allocate a normal ring, but also setup * the event ring segment table (ERST). Section 4.9.3. */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - flags); - if (!xhci->event_ring) - goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) + if (xhci_event_ring_init(xhci, GFP_KERNEL)) goto fail; - xhci->erst.entries = dma_alloc_coherent(dev, - sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, - GFP_KERNEL); - if (!xhci->erst.entries) + if (xhci_check_trb_in_td_math(xhci) < 0) goto fail; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Allocated event ring segment table at 0x%llx", - (unsigned long long)dma); - - memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); - xhci->erst.num_entries = ERST_NUM_SEGS; - xhci->erst.erst_dma_addr = dma; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx", - xhci->erst.num_entries, - xhci->erst.entries, - (unsigned long long)xhci->erst.erst_dma_addr); - - /* set ring base address and size for each segment table entry */ - for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { - struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = cpu_to_le64(seg->dma); - entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); - entry->rsvd = 0; - seg = seg->next; - } - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); - - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); - xhci_print_ir_set(xhci, 0); /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e57340a86eb3..42f74d55e3bd 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4968,6 +4968,61 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) } EXPORT_SYMBOL_GPL(xhci_gen_setup); +dma_addr_t xhci_get_sec_event_ring_dma_addr(struct usb_hcd *hcd, + unsigned intr_num) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (intr_num > xhci->max_interrupters) { + xhci_err(xhci, "intr num %d > max intrs %d\n", intr_num, + xhci->max_interrupters); + return 0; + } + + if (!(xhci->xhc_state & XHCI_STATE_HALTED) && + xhci->sec_event_ring && xhci->sec_event_ring[intr_num] + && xhci->sec_event_ring[intr_num]->first_seg) + return xhci->sec_event_ring[intr_num]->first_seg->dma; + + return 0; +} + +dma_addr_t xhci_get_dcba_dma_addr(struct usb_hcd *hcd, + struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (!(xhci->xhc_state & XHCI_STATE_HALTED) && xhci->dcbaa) + return xhci->dcbaa->dev_context_ptrs[udev->slot_id]; + + return 0; +} + +dma_addr_t xhci_get_xfer_ring_dma_addr(struct usb_hcd *hcd, + struct usb_device *udev, struct usb_host_endpoint *ep) +{ + int ret; + unsigned int ep_index; + struct xhci_virt_device *virt_dev; + + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); + if (ret <= 0) { + xhci_err(xhci, "%s: invalid args\n", __func__); + return 0; + } + + virt_dev = xhci->devs[udev->slot_id]; + ep_index = xhci_get_endpoint_index(&ep->desc); + + if (virt_dev->eps[ep_index].ring && + virt_dev->eps[ep_index].ring->first_seg) + return virt_dev->eps[ep_index].ring->first_seg->dma; + + return 0; +} + static const struct hc_driver xhci_hc_driver = { .description = "xhci-hcd", .product_desc = "xHCI Host Controller", @@ -5027,6 +5082,11 @@ static const struct hc_driver xhci_hc_driver = { .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, .find_raw_port_number = xhci_find_raw_port_number, + .sec_event_ring_setup = xhci_sec_event_ring_setup, + .sec_event_ring_cleanup = xhci_sec_event_ring_cleanup, + .get_sec_event_ring_dma_addr = xhci_get_sec_event_ring_dma_addr, + .get_xfer_ring_dma_addr = xhci_get_xfer_ring_dma_addr, + .get_dcba_dma_addr = xhci_get_dcba_dma_addr, }; void xhci_init_driver(struct hc_driver *drv, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0b9451250e33..a6e94e029a10 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1514,6 +1514,9 @@ struct xhci_hcd { /* Our HCD's current interrupter register set */ struct xhci_intr_reg __iomem *ir_set; + /* secondary interrupter */ + struct xhci_intr_reg __iomem **sec_ir_set; + /* Cached register copies of read-only HC data */ __u32 hcs_params1; __u32 hcs_params2; @@ -1554,6 +1557,11 @@ struct xhci_hcd { struct xhci_command *current_cmd; struct xhci_ring *event_ring; struct xhci_erst erst; + + /* secondary event ring and erst */ + struct xhci_ring **sec_event_ring; + struct xhci_erst *sec_erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; /* Store LPM test failed devices' information */ @@ -1811,6 +1819,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, void xhci_urb_free_priv(struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); +int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned intr_num); +int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5857f8ee8317..170f3ce3e881 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -26,7 +26,7 @@ obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o obj-$(CONFIG_USB_MSM_HSPHY) += phy-msm-hsusb.o obj-$(CONFIG_USB_MSM_SSPHY) += phy-msm-ssusb.o obj-$(CONFIG_USB_MSM_SSPHY_QMP) += phy-msm-ssusb-qmp.o -obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o +obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o phy-msm-qusb-v2.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c new file mode 100644 index 000000000000..1bc6b6ba4517 --- /dev/null +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -0,0 +1,955 @@ +/* + * Copyright (c) 2014-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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/clk/msm-clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/usb/phy.h> +#include <linux/usb/msm_hsusb.h> + +#define QUSB2PHY_PWR_CTRL1 0x210 +#define PWR_CTRL1_POWR_DOWN BIT(0) + +#define QUSB2PHY_PLL_COMMON_STATUS_ONE 0x1A0 +#define CORE_READY_STATUS BIT(0) + +/* In case Efuse register shows zero, use this value */ +#define TUNE2_DEFAULT_HIGH_NIBBLE 0xB +#define TUNE2_DEFAULT_LOW_NIBBLE 0x3 + +/* Get TUNE2's high nibble value read from efuse */ +#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask) + +#define QUSB2PHY_INTR_CTRL 0x22C +#define DMSE_INTR_HIGH_SEL BIT(4) +#define DPSE_INTR_HIGH_SEL BIT(3) +#define CHG_DET_INTR_EN BIT(2) +#define DMSE_INTR_EN BIT(1) +#define DPSE_INTR_EN BIT(0) + +#define QUSB2PHY_INTR_STAT 0x230 +#define DMSE_INTERRUPT BIT(1) +#define DPSE_INTERRUPT BIT(0) + +#define QUSB2PHY_PORT_TUNE2 0x240 + +#define HS_PHY_CTRL_REG 0x10 +#define UTMI_OTG_VBUS_VALID BIT(20) +#define SW_SESSVLD_SEL BIT(28) + +#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ +#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ +#define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */ + +#define QUSB2PHY_3P3_VOL_MIN 3075000 /* uV */ +#define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */ +#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */ + +unsigned int phy_tune2; +module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); + +struct qusb_phy { + struct usb_phy phy; + void __iomem *base; + void __iomem *qscratch_base; + void __iomem *tune2_efuse_reg; + + struct clk *ref_clk_src; + struct clk *ref_clk; + struct clk *cfg_ahb_clk; + struct clk *phy_reset; + + struct regulator *vdd; + struct regulator *vdda33; + struct regulator *vdda18; + int vdd_levels[3]; /* none, low, high */ + int init_seq_len; + int *qusb_phy_init_seq; + + u32 tune2_val; + int tune2_efuse_bit_pos; + int tune2_efuse_num_of_bits; + + bool power_enabled; + bool clocks_enabled; + bool cable_connected; + bool suspended; + bool rm_pulldown; + + struct regulator_desc dpdm_rdesc; + struct regulator_dev *dpdm_rdev; + + /* emulation targets specific */ + void __iomem *emu_phy_base; + bool emulation; + int *emu_init_seq; + int emu_init_seq_len; + int *phy_pll_reset_seq; + int phy_pll_reset_seq_len; + int *emu_dcm_reset_seq; + int emu_dcm_reset_seq_len; +}; + +static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) +{ + dev_dbg(qphy->phy.dev, "%s(): clocks_enabled:%d on:%d\n", + __func__, qphy->clocks_enabled, on); + + if (!qphy->clocks_enabled && on) { + clk_prepare_enable(qphy->ref_clk_src); + clk_prepare_enable(qphy->ref_clk); + clk_prepare_enable(qphy->cfg_ahb_clk); + qphy->clocks_enabled = true; + } + + if (qphy->clocks_enabled && !on) { + clk_disable_unprepare(qphy->ref_clk); + clk_disable_unprepare(qphy->ref_clk_src); + clk_disable_unprepare(qphy->cfg_ahb_clk); + qphy->clocks_enabled = false; + } + + dev_dbg(qphy->phy.dev, "%s(): clocks_enabled:%d\n", __func__, + qphy->clocks_enabled); +} + +static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) +{ + int min, ret; + + min = high ? 1 : 0; /* low or none? */ + ret = regulator_set_voltage(qphy->vdd, qphy->vdd_levels[min], + qphy->vdd_levels[2]); + if (ret) { + dev_err(qphy->phy.dev, "unable to set voltage for qusb vdd\n"); + return ret; + } + + dev_dbg(qphy->phy.dev, "min_vol:%d max_vol:%d\n", + qphy->vdd_levels[min], qphy->vdd_levels[2]); + return ret; +} + +static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on, + bool toggle_vdd) +{ + int ret = 0; + + dev_dbg(qphy->phy.dev, "%s turn %s regulators. power_enabled:%d\n", + __func__, on ? "on" : "off", qphy->power_enabled); + + if (toggle_vdd && qphy->power_enabled == on) { + dev_dbg(qphy->phy.dev, "PHYs' regulators are already ON.\n"); + return 0; + } + + if (!on) + goto disable_vdda33; + + if (toggle_vdd) { + ret = qusb_phy_config_vdd(qphy, true); + if (ret) { + dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", + ret); + goto err_vdd; + } + + ret = regulator_enable(qphy->vdd); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable VDD\n"); + goto unconfig_vdd; + } + } + + ret = regulator_set_load(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD); + if (ret < 0) { + dev_err(qphy->phy.dev, "Unable to set HPM of vdda18:%d\n", ret); + goto disable_vdd; + } + + ret = regulator_set_voltage(qphy->vdda18, QUSB2PHY_1P8_VOL_MIN, + QUSB2PHY_1P8_VOL_MAX); + if (ret) { + dev_err(qphy->phy.dev, + "Unable to set voltage for vdda18:%d\n", ret); + goto put_vdda18_lpm; + } + + ret = regulator_enable(qphy->vdda18); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable vdda18:%d\n", ret); + goto unset_vdda18; + } + + ret = regulator_set_load(qphy->vdda33, QUSB2PHY_3P3_HPM_LOAD); + if (ret < 0) { + dev_err(qphy->phy.dev, "Unable to set HPM of vdda33:%d\n", ret); + goto disable_vdda18; + } + + ret = regulator_set_voltage(qphy->vdda33, QUSB2PHY_3P3_VOL_MIN, + QUSB2PHY_3P3_VOL_MAX); + if (ret) { + dev_err(qphy->phy.dev, + "Unable to set voltage for vdda33:%d\n", ret); + goto put_vdda33_lpm; + } + + ret = regulator_enable(qphy->vdda33); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable vdda33:%d\n", ret); + goto unset_vdd33; + } + + if (toggle_vdd) + qphy->power_enabled = true; + + pr_debug("%s(): QUSB PHY's regulators are turned ON.\n", __func__); + return ret; + +disable_vdda33: + ret = regulator_disable(qphy->vdda33); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdda33:%d\n", ret); + +unset_vdd33: + ret = regulator_set_voltage(qphy->vdda33, 0, QUSB2PHY_3P3_VOL_MAX); + if (ret) + dev_err(qphy->phy.dev, + "Unable to set (0) voltage for vdda33:%d\n", ret); + +put_vdda33_lpm: + ret = regulator_set_load(qphy->vdda33, 0); + if (ret < 0) + dev_err(qphy->phy.dev, "Unable to set (0) HPM of vdda33\n"); + +disable_vdda18: + ret = regulator_disable(qphy->vdda18); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdda18:%d\n", ret); + +unset_vdda18: + ret = regulator_set_voltage(qphy->vdda18, 0, QUSB2PHY_1P8_VOL_MAX); + if (ret) + dev_err(qphy->phy.dev, + "Unable to set (0) voltage for vdda18:%d\n", ret); + +put_vdda18_lpm: + ret = regulator_set_load(qphy->vdda18, 0); + if (ret < 0) + dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n"); + +disable_vdd: + if (toggle_vdd) { + ret = regulator_disable(qphy->vdd); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", + ret); + +unconfig_vdd: + ret = qusb_phy_config_vdd(qphy, false); + if (ret) + dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", + ret); + } +err_vdd: + if (toggle_vdd) + qphy->power_enabled = false; + dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n"); + return ret; +} + +static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret = 0; + + dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n", + __func__, value, qphy->rm_pulldown); + + switch (value) { + case POWER_SUPPLY_DP_DM_DPF_DMF: + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n"); + if (!qphy->rm_pulldown) { + ret = qusb_phy_enable_power(qphy, true, false); + if (ret >= 0) { + qphy->rm_pulldown = true; + dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n", + qphy->rm_pulldown); + } + } + + break; + + case POWER_SUPPLY_DP_DM_DPR_DMR: + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n"); + if (qphy->rm_pulldown) { + ret = qusb_phy_enable_power(qphy, false, false); + if (ret >= 0) { + qphy->rm_pulldown = false; + dev_dbg(phy->dev, "DP_DM_R: rm_pulldown:%d\n", + qphy->rm_pulldown); + } + } + break; + + default: + ret = -EINVAL; + dev_err(phy->dev, "Invalid power supply property(%d)\n", value); + break; + } + + return ret; +} + +static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) +{ + u8 num_of_bits; + u32 bit_mask = 1; + + pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, + qphy->tune2_efuse_num_of_bits, + qphy->tune2_efuse_bit_pos); + + /* get bit mask based on number of bits to use with efuse reg */ + if (qphy->tune2_efuse_num_of_bits) { + num_of_bits = qphy->tune2_efuse_num_of_bits; + bit_mask = (bit_mask << num_of_bits) - 1; + } + + /* + * Read EFUSE register having TUNE2 parameter's high nibble. + * If efuse register shows value as 0x0, then use default value + * as 0xB as high nibble. Otherwise use efuse register based + * value for this purpose. + */ + qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg); + pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n", + __func__, bit_mask, qphy->tune2_val); + + qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val, + qphy->tune2_efuse_bit_pos, bit_mask); + + if (!qphy->tune2_val) + qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE; + + /* Get TUNE2 byte value using high and low nibble value */ + qphy->tune2_val = ((qphy->tune2_val << 0x4) | + TUNE2_DEFAULT_LOW_NIBBLE); +} + +static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, + unsigned long delay) +{ + int i; + + pr_debug("Seq count:%d\n", cnt); + for (i = 0; i < cnt; i = i+2) { + pr_debug("write 0x%02x to 0x%02x\n", seq[i], seq[i+1]); + writel_relaxed(seq[i], base + seq[i+1]); + if (delay) + usleep_range(delay, (delay + 2000)); + } +} + +static int qusb_phy_init(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret; + u8 reg; + + dev_dbg(phy->dev, "%s\n", __func__); + + ret = qusb_phy_enable_power(qphy, true, true); + if (ret) + return ret; + + qusb_phy_enable_clocks(qphy, true); + + /* Perform phy reset */ + clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); + usleep_range(100, 150); + clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); + + if (qphy->emulation) { + if (qphy->emu_init_seq) + qusb_phy_write_seq(qphy->emu_phy_base, + qphy->emu_init_seq, qphy->emu_init_seq_len, 0); + + if (qphy->qusb_phy_init_seq) + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, + qphy->init_seq_len, 0); + + /* Wait for 5ms as per QUSB2 RUMI sequence */ + usleep_range(5000, 7000); + + if (qphy->phy_pll_reset_seq) + qusb_phy_write_seq(qphy->base, qphy->phy_pll_reset_seq, + qphy->phy_pll_reset_seq_len, 10000); + + if (qphy->emu_dcm_reset_seq) + qusb_phy_write_seq(qphy->emu_phy_base, + qphy->emu_dcm_reset_seq, + qphy->emu_dcm_reset_seq_len, 10000); + + return 0; + } + + /* Disable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) | + PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + if (qphy->qusb_phy_init_seq) + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, + qphy->init_seq_len, 0); + /* + * Check for EFUSE value only if tune2_efuse_reg is available + * and try to read EFUSE value only once i.e. not every USB + * cable connect case. + */ + if (qphy->tune2_efuse_reg) { + if (!qphy->tune2_val) + qusb_phy_get_tune2_param(qphy); + + pr_debug("%s(): Programming TUNE2 parameter as:%x\n", __func__, + qphy->tune2_val); + writel_relaxed(qphy->tune2_val, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + + /* If phy_tune2 modparam set, override tune2 value */ + if (phy_tune2) { + pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n", + __func__, phy_tune2); + writel_relaxed(phy_tune2, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + + /* ensure above writes are completed before re-enabling PHY */ + wmb(); + + /* Enable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) & + ~PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Ensure above write is completed before turning ON ref clk */ + wmb(); + + /* Require to get phy pll lock successfully */ + usleep_range(150, 160); + + reg = readb_relaxed(qphy->base + QUSB2PHY_PLL_COMMON_STATUS_ONE); + dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg); + if (!(reg & CORE_READY_STATUS)) { + dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg); + WARN_ON(1); + } + return 0; +} + +static void qusb_phy_shutdown(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + dev_dbg(phy->dev, "%s\n", __func__); + + qusb_phy_enable_clocks(qphy, true); + + /* Disable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) | + PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Makes sure that above write goes through */ + wmb(); + + qusb_phy_enable_clocks(qphy, false); +} +/** + * Performs QUSB2 PHY suspend/resume functionality. + * + * @uphy - usb phy pointer. + * @suspend - to enable suspend or not. 1 - suspend, 0 - resume + * + */ +static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + u32 linestate = 0, intr_mask = 0; + + if (qphy->suspended && suspend) { + dev_dbg(phy->dev, "%s: USB PHY is already suspended\n", + __func__); + return 0; + } + + if (suspend) { + /* Bus suspend case */ + if (qphy->cable_connected || + (qphy->phy.flags & PHY_HOST_MODE)) { + /* Disable all interrupts */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + linestate = readl_relaxed(qphy->base + + QUSB2PHY_INTR_STAT); + /* + * D+/D- interrupts are level-triggered, but we are + * only interested if the line state changes, so enable + * the high/low trigger based on current state. In + * other words, enable the triggers _opposite_ of what + * the current D+/D- levels are. + * e.g. if currently D+ high, D- low (HS 'J'/Suspend), + * configure the mask to trigger on D+ low OR D- high + */ + intr_mask = DMSE_INTERRUPT | DPSE_INTERRUPT; + if (!(linestate & DPSE_INTR_EN)) /* D+ low */ + intr_mask |= DPSE_INTR_HIGH_SEL; + if (!(linestate & DMSE_INTR_EN)) /* D- low */ + intr_mask |= DMSE_INTR_HIGH_SEL; + + writel_relaxed(intr_mask, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Makes sure that above write goes through */ + wmb(); + qusb_phy_enable_clocks(qphy, false); + } else { /* Cable disconnect case */ + /* Disable all interrupts */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Put PHY into non-driving mode */ + writel_relaxed(0x23, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Makes sure that above write goes through */ + wmb(); + + qusb_phy_enable_clocks(qphy, false); + qusb_phy_enable_power(qphy, false, true); + } + qphy->suspended = true; + } else { + /* Bus resume case */ + if (qphy->cable_connected || + (qphy->phy.flags & PHY_HOST_MODE)) { + qusb_phy_enable_clocks(qphy, true); + /* Clear all interrupts on resume */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Makes sure that above write goes through */ + wmb(); + } else { /* Cable connect case */ + qusb_phy_enable_power(qphy, true, true); + qusb_phy_enable_clocks(qphy, true); + } + qphy->suspended = false; + } + + return 0; +} + +static void qusb_write_readback(void *base, u32 offset, + const u32 mask, u32 val) +{ + u32 write_val, tmp = readl_relaxed(base + offset); + + tmp &= ~mask; /* retain other bits */ + write_val = tmp | val; + + writel_relaxed(write_val, base + offset); + + /* Read back to see if val was written */ + tmp = readl_relaxed(base + offset); + tmp &= mask; /* clear other bits */ + + if (tmp != val) + pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", + __func__, val, offset); +} + +static int qusb_phy_notify_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + qphy->cable_connected = true; + + dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + + /* Set OTG VBUS Valid from HSPHY to controller */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + UTMI_OTG_VBUS_VALID, + UTMI_OTG_VBUS_VALID); + + /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + SW_SESSVLD_SEL, SW_SESSVLD_SEL); + + dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + return 0; +} + +static int qusb_phy_notify_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + qphy->cable_connected = false; + + dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + + /* Set OTG VBUS Valid from HSPHY to controller */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + UTMI_OTG_VBUS_VALID, 0); + + /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + SW_SESSVLD_SEL, 0); + + dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + return 0; +} + +static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s\n", __func__); + return qusb_phy_update_dpdm(&qphy->phy, POWER_SUPPLY_DP_DM_DPF_DMF); +} + +static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s\n", __func__); + return qusb_phy_update_dpdm(&qphy->phy, POWER_SUPPLY_DP_DM_DPR_DMR); +} + +static int qusb_phy_dpdm_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s qphy->rm_pulldown = %d\n", __func__, + qphy->rm_pulldown); + return qphy->rm_pulldown; +} + +static struct regulator_ops qusb_phy_dpdm_regulator_ops = { + .enable = qusb_phy_dpdm_regulator_enable, + .disable = qusb_phy_dpdm_regulator_disable, + .is_enabled = qusb_phy_dpdm_regulator_is_enabled, +}; + +static int qusb_phy_regulator_init(struct qusb_phy *qphy) +{ + struct device *dev = qphy->phy.dev; + struct regulator_config cfg = {}; + struct regulator_init_data *init_data; + + init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL); + if (!init_data) + return -ENOMEM; + + init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS; + qphy->dpdm_rdesc.owner = THIS_MODULE; + qphy->dpdm_rdesc.type = REGULATOR_VOLTAGE; + qphy->dpdm_rdesc.ops = &qusb_phy_dpdm_regulator_ops; + qphy->dpdm_rdesc.name = kbasename(dev->of_node->full_name); + + cfg.dev = dev; + cfg.init_data = init_data; + cfg.driver_data = qphy; + cfg.of_node = dev->of_node; + + qphy->dpdm_rdev = devm_regulator_register(dev, &qphy->dpdm_rdesc, &cfg); + if (IS_ERR(qphy->dpdm_rdev)) + return PTR_ERR(qphy->dpdm_rdev); + + return 0; +} + +static int qusb_phy_probe(struct platform_device *pdev) +{ + struct qusb_phy *qphy; + struct device *dev = &pdev->dev; + struct resource *res; + int ret = 0, size = 0; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->phy.dev = dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qusb_phy_base"); + qphy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->base)) + return PTR_ERR(qphy->base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qscratch_base"); + if (res) { + qphy->qscratch_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->qscratch_base)) { + dev_dbg(dev, "couldn't ioremap qscratch_base\n"); + qphy->qscratch_base = NULL; + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "emu_phy_base"); + if (res) { + qphy->emu_phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->emu_phy_base)) { + dev_dbg(dev, "couldn't ioremap emu_phy_base\n"); + qphy->emu_phy_base = NULL; + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "tune2_efuse_addr"); + if (res) { + qphy->tune2_efuse_reg = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!IS_ERR_OR_NULL(qphy->tune2_efuse_reg)) { + ret = of_property_read_u32(dev->of_node, + "qcom,tune2-efuse-bit-pos", + &qphy->tune2_efuse_bit_pos); + if (!ret) { + ret = of_property_read_u32(dev->of_node, + "qcom,tune2-efuse-num-bits", + &qphy->tune2_efuse_num_of_bits); + } + + if (ret) { + dev_err(dev, + "DT Value for tune2 efuse is invalid.\n"); + return -EINVAL; + } + } + } + + qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); + if (IS_ERR(qphy->ref_clk_src)) + dev_dbg(dev, "clk get failed for ref_clk_src\n"); + + qphy->ref_clk = devm_clk_get(dev, "ref_clk"); + if (IS_ERR(qphy->ref_clk)) + dev_dbg(dev, "clk get failed for ref_clk\n"); + else + clk_set_rate(qphy->ref_clk, 19200000); + + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(qphy->cfg_ahb_clk)) + return PTR_ERR(qphy->cfg_ahb_clk); + + qphy->phy_reset = devm_clk_get(dev, "phy_reset"); + if (IS_ERR(qphy->phy_reset)) + return PTR_ERR(qphy->phy_reset); + + qphy->emulation = of_property_read_bool(dev->of_node, + "qcom,emulation"); + + of_get_property(dev->of_node, "qcom,emu-init-seq", &size); + if (size) { + qphy->emu_init_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->emu_init_seq) { + qphy->emu_init_seq_len = + (size / sizeof(*qphy->emu_init_seq)); + if (qphy->emu_init_seq_len % 2) { + dev_err(dev, "invalid emu_init_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,qemu-init-seq", + qphy->emu_init_seq, + qphy->emu_init_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for emu_init_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,phy-pll-reset-seq", &size); + if (size) { + qphy->phy_pll_reset_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->phy_pll_reset_seq) { + qphy->phy_pll_reset_seq_len = + (size / sizeof(*qphy->phy_pll_reset_seq)); + if (qphy->phy_pll_reset_seq_len % 2) { + dev_err(dev, "invalid phy_pll_reset_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,phy-pll-reset-seq", + qphy->phy_pll_reset_seq, + qphy->phy_pll_reset_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for phy_pll_reset_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,emu-dcm-reset-seq", &size); + if (size) { + qphy->emu_dcm_reset_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->emu_dcm_reset_seq) { + qphy->emu_dcm_reset_seq_len = + (size / sizeof(*qphy->emu_dcm_reset_seq)); + if (qphy->emu_dcm_reset_seq_len % 2) { + dev_err(dev, "invalid emu_dcm_reset_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,emu-dcm-reset-seq", + qphy->emu_dcm_reset_seq, + qphy->emu_dcm_reset_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for emu_dcm_reset_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,qusb-phy-init-seq", &size); + if (size) { + qphy->qusb_phy_init_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->qusb_phy_init_seq) { + qphy->init_seq_len = + (size / sizeof(*qphy->qusb_phy_init_seq)); + if (qphy->init_seq_len % 2) { + dev_err(dev, "invalid init_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,qusb-phy-init-seq", + qphy->qusb_phy_init_seq, + qphy->init_seq_len); + } else { + dev_err(dev, + "error allocating memory for phy_init_seq\n"); + } + } + + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", + (u32 *) qphy->vdd_levels, + ARRAY_SIZE(qphy->vdd_levels)); + if (ret) { + dev_err(dev, "error reading qcom,vdd-voltage-level property\n"); + return ret; + } + + qphy->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(qphy->vdd)) { + dev_err(dev, "unable to get vdd supply\n"); + return PTR_ERR(qphy->vdd); + } + + qphy->vdda33 = devm_regulator_get(dev, "vdda33"); + if (IS_ERR(qphy->vdda33)) { + dev_err(dev, "unable to get vdda33 supply\n"); + return PTR_ERR(qphy->vdda33); + } + + qphy->vdda18 = devm_regulator_get(dev, "vdda18"); + if (IS_ERR(qphy->vdda18)) { + dev_err(dev, "unable to get vdda18 supply\n"); + return PTR_ERR(qphy->vdda18); + } + + platform_set_drvdata(pdev, qphy); + + qphy->phy.label = "msm-qusb-phy-v2"; + qphy->phy.init = qusb_phy_init; + qphy->phy.set_suspend = qusb_phy_set_suspend; + qphy->phy.shutdown = qusb_phy_shutdown; + qphy->phy.type = USB_PHY_TYPE_USB2; + + if (qphy->qscratch_base) { + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; + } + + ret = usb_add_phy_dev(&qphy->phy); + if (ret) + return ret; + + ret = qusb_phy_regulator_init(qphy); + if (ret) + usb_remove_phy(&qphy->phy); + + return ret; +} + +static int qusb_phy_remove(struct platform_device *pdev) +{ + struct qusb_phy *qphy = platform_get_drvdata(pdev); + + usb_remove_phy(&qphy->phy); + + if (qphy->clocks_enabled) { + clk_disable_unprepare(qphy->cfg_ahb_clk); + clk_disable_unprepare(qphy->ref_clk); + clk_disable_unprepare(qphy->ref_clk_src); + qphy->clocks_enabled = false; + } + + qusb_phy_enable_power(qphy, false, true); + + return 0; +} + +static const struct of_device_id qusb_phy_id_table[] = { + { .compatible = "qcom,qusb2phy-v2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qusb_phy_id_table); + +static struct platform_driver qusb_phy_driver = { + .probe = qusb_phy_probe, + .remove = qusb_phy_remove, + .driver = { + .name = "msm-qusb-phy-v2", + .of_match_table = of_match_ptr(qusb_phy_id_table), + }, +}; + +module_platform_driver(qusb_phy_driver); + +MODULE_DESCRIPTION("MSM QUSB2 PHY v2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 33f18cd28de9..3d773371713d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -287,7 +287,7 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) { - return hdmi_ctrl->dc_support && + return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support && (hdmi_edid_get_deep_color( hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)) & BIT(1)); } @@ -2204,8 +2204,12 @@ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) } } - DEV_DBG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__, - hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON"); + if (hdmi_ctrl->hdmi_tx_version >= HDMI_TX_VERSION_403) + hdmi_ctrl->dc_feature_on = true; + + DEV_DBG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__, + hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON", + hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); if (hdmi_disabled) { DEV_ERR("%s: HDMI disabled\n", __func__); @@ -2837,6 +2841,9 @@ static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl) rate = timing->pixel_freq / rate_ratio; + if (hdmi_tx_dc_support(hdmi_ctrl)) + rate += rate >> 2; + end: return rate; } diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index b606ffedaa26..462edac31c09 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -126,6 +126,7 @@ struct hdmi_tx_ctrl { bool hdcp22_present; bool power_data_enable[HDMI_TX_MAX_PM]; bool dc_support; + bool dc_feature_on; void (*hdmi_tx_hpd_done)(void *data); void *downstream_data; diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h index 353466f6c108..ae36a163ed21 100644 --- a/include/linux/leds-qpnp-flash-v2.h +++ b/include/linux/leds-qpnp-flash-v2.h @@ -26,13 +26,14 @@ struct flash_node_data { struct pinctrl_state *gpio_state_active; struct pinctrl_state *gpio_state_suspend; int ires_ua; - u16 prgm_current; + int max_current; + int current_ma; u8 duration; u8 id; u8 type; u8 ires; u8 hdrm_val; - u8 brightness; + u8 current_reg_val; bool led_on; }; diff --git a/include/linux/usb.h b/include/linux/usb.h index b79925dd2b41..246945be000c 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -734,6 +734,17 @@ static inline bool usb_device_no_sg_constraint(struct usb_device *udev) /* for drivers using iso endpoints */ extern int usb_get_current_frame_number(struct usb_device *usb_dev); +extern int usb_sec_event_ring_setup(struct usb_device *dev, + unsigned intr_num); +extern int usb_sec_event_ring_cleanup(struct usb_device *dev, + unsigned intr_num); + +extern dma_addr_t +usb_get_sec_event_ring_dma_addr(struct usb_device *dev, + unsigned intr_num); +extern dma_addr_t usb_get_dcba_dma_addr(struct usb_device *dev); +extern dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev, + struct usb_host_endpoint *ep); /* Sets up a group of bulk endpoints to support multiple stream IDs. */ extern int usb_alloc_streams(struct usb_interface *interface, diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index f89c24bd53a4..3740366d9fc5 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -396,6 +396,14 @@ struct hc_driver { /* Call for power on/off the port if necessary */ int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable); + int (*sec_event_ring_setup)(struct usb_hcd *hcd, unsigned intr_num); + int (*sec_event_ring_cleanup)(struct usb_hcd *hcd, unsigned intr_num); + dma_addr_t (*get_sec_event_ring_dma_addr)(struct usb_hcd *hcd, + unsigned intr_num); + dma_addr_t (*get_xfer_ring_dma_addr)(struct usb_hcd *hcd, + struct usb_device *udev, struct usb_host_endpoint *ep); + dma_addr_t (*get_dcba_dma_addr)(struct usb_hcd *hcd, + struct usb_device *udev); }; static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) @@ -434,6 +442,17 @@ extern int usb_hcd_alloc_bandwidth(struct usb_device *udev, struct usb_host_interface *old_alt, struct usb_host_interface *new_alt); extern int usb_hcd_get_frame_number(struct usb_device *udev); +extern int usb_hcd_sec_event_ring_setup(struct usb_device *udev, + unsigned intr_num); +extern int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev, + unsigned intr_num); +extern dma_addr_t +usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev, + unsigned intr_num); +extern dma_addr_t usb_hcd_get_dcba_dma_addr(struct usb_device *udev); +extern dma_addr_t +usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev, + struct usb_host_endpoint *ep); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name); diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index 092551d0cc16..fe70daa772df 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -202,5 +202,24 @@ struct msm_camera_user_buf_cont_t { unsigned int buf_idx[MSM_CAMERA_MAX_USER_BUFF_CNT]; }; +struct msm_camera_return_buf { + __u32 index; + __u32 reserved; +}; + +#define MSM_CAMERA_PRIV_IOCTL_ID_BASE 0 +#define MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF 1 + +struct msm_camera_private_ioctl_arg { + __u32 id; + __u32 size; + __u32 result; + __u32 reserved; + __user __u64 ioctl_ptr; +}; + +#define VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_private_ioctl_arg) + #endif diff --git a/include/uapi/media/msmb_generic_buf_mgr.h b/include/uapi/media/msmb_generic_buf_mgr.h index a68b174b97bb..2961cae1e7c1 100644 --- a/include/uapi/media/msmb_generic_buf_mgr.h +++ b/include/uapi/media/msmb_generic_buf_mgr.h @@ -34,6 +34,9 @@ struct msm_buf_mngr_main_cont_info { int32_t cont_fd; }; +#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_BASE 0 +#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX 1 + #define VIDIOC_MSM_BUF_MNGR_GET_BUF \ _IOWR('V', BASE_VIDIOC_PRIVATE + 33, struct msm_buf_mngr_info) @@ -55,5 +58,9 @@ struct msm_buf_mngr_main_cont_info { #define VIDIOC_MSM_BUF_MNGR_FLUSH \ _IOWR('V', BASE_VIDIOC_PRIVATE + 39, struct msm_buf_mngr_info) +#define VIDIOC_MSM_BUF_MNGR_IOCTL_CMD \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 40, \ + struct msm_camera_private_ioctl_arg) + #endif diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index c941729be2aa..f496559f0632 100755 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -7511,9 +7511,9 @@ static int tasha_mad_input_put(struct snd_kcontrol *kcontrol, "%s: tasha input widget = %s\n", __func__, mad_input_widget); - for (i = 0; i < card->num_dapm_routes; i++) { - if (!strcmp(card->dapm_routes[i].sink, mad_input_widget)) { - source_widget = card->dapm_routes[i].source; + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; if (!source_widget) { dev_err(codec->dev, "%s: invalid source widget\n", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c index 9b2ef351e4f2..30cdda80019c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -29,7 +29,9 @@ #include "msm-pcm-routing-v2.h" #define LOOPBACK_VOL_MAX_STEPS 0x2000 +#define LOOPBACK_SESSION_MAX 4 +static DEFINE_MUTEX(loopback_session_lock); static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0, LOOPBACK_VOL_MAX_STEPS); @@ -51,7 +53,21 @@ struct msm_pcm_loopback { int volume; }; +struct fe_dai_session_map { + char stream_name[32]; + struct msm_pcm_loopback *loopback_priv; +}; + +static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = { + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, +}; + static void stop_pcm(struct msm_pcm_loopback *pcm); +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm); static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, void *priv_data) @@ -111,18 +127,76 @@ static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume) return rc; } +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm) +{ + int ret = 0; + int n, index = -1; + + dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__, + rtd->dai_link->stream_name); + + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + *pcm = session_map[n].loopback_priv; + goto exit; + } + /* + * Store the min index value for allocating a new session. + * Here, if session stream name is not found in the + * existing entries after the loop iteration, then this + * index will be used to allocate the new session. + * This index variable is expected to point to the topmost + * available free session. + */ + if (!(session_map[n].stream_name[0]) && (index < 0)) + index = n; + } + + if (index < 0) { + dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n", + __func__); + ret = -EAGAIN; + goto exit; + } + + session_map[index].loopback_priv = kzalloc( + sizeof(struct msm_pcm_loopback), GFP_KERNEL); + if (!session_map[index].loopback_priv) { + ret = -ENOMEM; + goto exit; + } + + strlcpy(session_map[index].stream_name, + rtd->dai_link->stream_name, + sizeof(session_map[index].stream_name)); + dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n", + __func__, session_map[index].stream_name, index); + + mutex_init(&session_map[index].loopback_priv->lock); + *pcm = session_map[index].loopback_priv; +exit: + mutex_unlock(&loopback_session_lock); + return ret; +} + static int msm_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct msm_pcm_loopback *pcm; + struct msm_pcm_loopback *pcm = NULL; int ret = 0; uint16_t bits_per_sample = 16; struct msm_pcm_routing_evt event; struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; uint32_t param_id; - pcm = dev_get_drvdata(rtd->platform->dev); + ret = msm_pcm_loopback_get_session(rtd, &pcm); + if (ret) + return ret; + mutex_lock(&pcm->lock); pcm->volume = 0x2000; @@ -230,7 +304,8 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct msm_pcm_loopback *pcm = runtime->private_data; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - int ret = 0; + int ret = 0, n; + bool found = false; mutex_lock(&pcm->lock); @@ -247,6 +322,29 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) stop_pcm(pcm); } + if (!pcm->instance) { + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + found = true; + break; + } + } + if (found) { + memset(session_map[n].stream_name, 0, + sizeof(session_map[n].stream_name)); + mutex_unlock(&pcm->lock); + mutex_destroy(&session_map[n].loopback_priv->lock); + session_map[n].loopback_priv = NULL; + kfree(pcm); + dev_dbg(rtd->platform->dev, "%s: stream freed %s\n", + __func__, rtd->dai_link->stream_name); + mutex_unlock(&loopback_session_lock); + return 0; + } + mutex_unlock(&loopback_session_lock); + } mutex_unlock(&pcm->lock); return ret; } @@ -368,33 +466,15 @@ static struct snd_soc_platform_driver msm_soc_platform = { static int msm_pcm_probe(struct platform_device *pdev) { - struct msm_pcm_loopback *pcm; - - dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev)); - pcm = kzalloc(sizeof(struct msm_pcm_loopback), GFP_KERNEL); - if (!pcm) { - dev_err(&pdev->dev, "%s Failed to allocate memory for pcm\n", - __func__); - return -ENOMEM; - } else { - mutex_init(&pcm->lock); - dev_set_drvdata(&pdev->dev, pcm); - } return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); } static int msm_pcm_remove(struct platform_device *pdev) { - struct msm_pcm_loopback *pcm; - - pcm = dev_get_drvdata(&pdev->dev); - mutex_destroy(&pcm->lock); - kfree(pcm); - snd_soc_unregister_platform(&pdev->dev); return 0; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c9e54acb06ec..cac2d4975a15 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -876,6 +876,12 @@ struct snd_soc_component *soc_find_component( { struct snd_soc_component *component; + if (!of_node && !name) { + pr_err("%s: Either of_node or name must be valid\n", + __func__); + return NULL; + } + lockdep_assert_held(&client_mutex); list_for_each_entry(component, &component_list, list) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b4ffe3904dcc..4ef20ba02cb4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -698,6 +698,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { + /* powered down playback stream now */ + snd_soc_dapm_stream_event(rtd, + SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } else { + /* start delayed pop wq here for playback streams */ + rtd->pop_wait = 1; + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } + } if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -713,20 +727,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (snd_soc_runtime_ignore_pmdown_time(rtd)) { - /* powered down playback stream now */ - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } else { - /* start delayed pop wq here for playback streams */ - rtd->pop_wait = 1; - queue_delayed_work(system_power_efficient_wq, - &rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); - } - } else { + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_STOP); diff --git a/sound/usb/card.c b/sound/usb/card.c index 7e1001c1cf1e..0a189d3b5efa 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -110,6 +110,71 @@ static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; +struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num, + unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio + **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip)) +{ + int idx; + struct snd_usb_stream *as; + struct snd_usb_substream *subs = NULL; + struct snd_usb_audio *chip = NULL; + + mutex_lock(®ister_mutex); + /* + * legacy audio snd card number assignment is dynamic. Hence + * search using chip->card->number + */ + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (!usb_chip[idx]) + continue; + if (usb_chip[idx]->card->number == card_num) { + chip = usb_chip[idx]; + break; + } + } + + if (!chip || atomic_read(&chip->shutdown)) { + pr_debug("%s: instance of usb crad # %d does not exist\n", + __func__, card_num); + goto err; + } + + if (pcm_idx >= chip->pcm_devs) { + pr_err("%s: invalid pcm dev number %u > %d\n", __func__, + pcm_idx, chip->pcm_devs); + goto err; + } + + if (direction > SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: invalid direction %u\n", __func__, direction); + goto err; + } + + list_for_each_entry(as, &chip->pcm_list, list) { + if (as->pcm_index == pcm_idx) { + subs = &as->substream[direction]; + if (subs->interface < 0 && !subs->data_endpoint && + !subs->sync_endpoint) { + pr_debug("%s: stream disconnected, bail out\n", + __func__); + subs = NULL; + goto err; + } + goto done; + } + } + +done: + chip->card_num = card_num; + chip->disconnect_cb = disconnect_cb; +err: + *uchip = chip; + if (!subs) + pr_debug("%s: substream instance not found\n", __func__); + mutex_unlock(®ister_mutex); + return subs; +} + /* * disconnect streams * called from usb_audio_disconnect() @@ -639,6 +704,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->num_interfaces <= 0) { usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); + if (chip->disconnect_cb) + chip->disconnect_cb(chip); snd_card_free_when_closed(card); } else { mutex_unlock(®ister_mutex); diff --git a/sound/usb/card.h b/sound/usb/card.h index 71778ca4b26a..7421ee53c29f 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -167,4 +167,8 @@ struct snd_usb_stream { struct list_head list; }; +struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num, + unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio + **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip)); + #endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9245f52d43bd..9a9b789a56ef 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -552,6 +552,64 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return 0; } +int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, + bool enable) +{ + struct audioformat *fmt; + struct usb_host_interface *alts; + struct usb_interface *iface; + int ret; + + if (!enable) { + if (subs->interface >= 0) { + usb_set_interface(subs->dev, subs->interface, 0); + subs->altset_idx = 0; + subs->interface = -1; + subs->cur_audiofmt = NULL; + } + + snd_usb_autosuspend(subs->stream->chip); + return 0; + } + + snd_usb_autoresume(subs->stream->chip); + fmt = find_format(subs); + if (!fmt) { + dev_dbg(&subs->dev->dev, + "cannot set format: format = %#x, rate = %d, channels = %d\n", + subs->pcm_format, subs->cur_rate, subs->channels); + return -EINVAL; + } + + subs->altset_idx = 0; + subs->interface = -1; + if (atomic_read(&subs->stream->chip->shutdown)) { + ret = -ENODEV; + } else { + ret = set_format(subs, fmt); + if (ret < 0) + return ret; + + iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); + alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->stream->chip, + subs->cur_audiofmt->iface, + alts, + subs->cur_audiofmt, + subs->cur_rate); + if (ret < 0) { + dev_err(&subs->dev->dev, "failed to set rate %d\n", + subs->cur_rate); + return ret; + } + } + + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + + return 0; +} + /* * Return the score of matching two audioformats. * Veto the audioformat if: diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index df7a003682ad..d581f94b3fbf 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -9,6 +9,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt); - +int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, + bool enable); #endif /* __USBAUDIO_PCM_H */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b665d85555cb..207ddb26f4ed 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -60,6 +60,8 @@ struct snd_usb_audio { bool autoclock; /* from the 'autoclock' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ + int card_num; /* cache pcm card number to use upon disconnect */ + void (*disconnect_cb)(struct snd_usb_audio *chip); }; #define usb_audio_err(chip, fmt, args...) \ |
