diff options
116 files changed, 4099 insertions, 1193 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/adv7481.txt b/Documentation/devicetree/bindings/arm/msm/adv7481.txt new file mode 100644 index 000000000000..974c0877ac30 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/adv7481.txt @@ -0,0 +1,54 @@ +ADV7481 chip driver (VIDEO_ADV7481) + +VIDEO_ADV7481 is a kernel platform driver that is used for video decoder +and dual mode HDMI/MHL receiver. + +The devicetree representation of the VIDEO_ADV7481 block should be: + +Required properties + +- compatible: "qcom,adv7481" +- reg: The i2c slave address of adv7481 device. +- qcom,cci-master: The i2c master id to be used for adv7481 driver. +- gpios: The GPIOs required to be configured for the driver. It should + be in the order I2C data line, i2c clock line, reset line, + interrupt 1, interrupt 2 and interrupt 3. +- cam_vdig-supply: Should contain regulator to be used for the digital + vdd. +- cam_vio-supply: Should contain regulator to be used for the IO vdd. +- cam_vana-supply: Should contain regulator from which analog voltage + is supplied. +- qcom,cam-vreg-name: Should specify array of regulator names required + for the device. +- qcom,cam-vreg-min-voltage: Should specify array of minimum voltage + level in uV for the regulators specified in the property + "qcom,cam-vreg-name". +- qcom,cam-vreg-max-voltage: Should specify array of maximum voltage + level in uV for the regulators specified in the property + "qcom,cam-vreg-name". +- qcom,cam-vreg-op-mode: Should specify array of current level in uA + for the regulators specified in the property "qcom,cam-vreg-name". + +Example: + + qcom,adv7481@70 { + compatible = "qcom,adv7481"; + reg = <0x70 0xff>; + cam_vdig-supply = <&vph_pwr_vreg>; + /* Cameras powered by PMIC: */ + cam_vio-supply = <&pm8994_lvs1>; + cam_vana-supply = <&pm8994_l17>; + /* Self-powered cameras: */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-min-voltage = <1300000 0 2500000>; + qcom,cam-vreg-max-voltage = <1300000 0 2500000>; + qcom,cam-vreg-op-mode = <105000 0 80000>; + qcom,cci-master = <0>; + qcom,slave-addr = <0x70>; + gpios = <&tlmm 17 0>, /* I2C SDA */ + <&tlmm 18 0>, /* I2C SCL */ + <&pm8994_gpios 4 0>, /* RST */ + <&pm8994_gpios 5 0>, /* INT1 */ + <&pm8994_gpios 6 0>, /* INT2 */ + <&pm8994_gpios 7 0>; /* INT3 */ + }; diff --git a/Documentation/devicetree/bindings/display/msm/cec.txt b/Documentation/devicetree/bindings/display/msm/cec.txt index ba51b0d1dd18..e5282f1f5e9e 100644 --- a/Documentation/devicetree/bindings/display/msm/cec.txt +++ b/Documentation/devicetree/bindings/display/msm/cec.txt @@ -5,6 +5,7 @@ various audiovisual products in a user environment. Required properties: - compatible: Must be "qcom,hdmi-cec". +- qcom,hdmi-dev: Phandle for the hdmi device node. - interrupt-parent: Must be the hdmi interrupt controller. - interrupts: Interrupt associated with cec. - reg: Physical base address and length of the controller's registers. @@ -41,6 +42,7 @@ Example: sde_hdmi_cec: qcom,hdmi-cec@c9a0000 { compatible = "qcom,hdmi-cec"; label = "sde_hdmi_cec"; + qcom,hdmi-dev = <&sde_hdmi>; interrupt-parent = <&sde_hdmi_tx>; interrupts = <1 0>; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt index 0d55389f3790..f28de379f84c 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt @@ -105,7 +105,9 @@ Optional properties: - qcom,platform-reset-gpio: Specifies the panel reset gpio. - qcom,platform-te-gpio: Specifies the gpio used for TE. - qcom,platform-bklight-en-gpio: Specifies the gpio used to enable display back-light -- qcom,platform-bklight-en-gpio-invert: Invert the gpio used to enable display back-light +- qcom,platform-bklight-en-gpio-invert: Boolean to invert the gpio used to enable display back-light +- qcom,platform-avdd-en-gpio: Specifies the gpio used to enable AMOLED AVDD +- qcom,platform-avdd-en-gpio-invert: Boolean to invert the gpio used to enable AMOLED AVDD - qcom,panel-mode-gpio: Specifies the GPIO to select video/command/single-port/dual-port mode of panel through gpio when it supports these modes. - pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt index 2e9d2dae51a2..4ba9060222c2 100644 --- a/Documentation/devicetree/bindings/fb/mdss-pll.txt +++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt @@ -15,9 +15,10 @@ Required properties: "qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2", "qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_hdmi_pll_8996_v3_1p8", "qcom,mdss_dsi_pll_8998", "qcom,mdss_dp_pll_8998", - "qcom,mdss_hdmi_pll_8998", "qcom,mdss_dsi_pll_sdm660", - "qcom,mdss_dp_pll_sdm660", "qcom,mdss_dsi_pll_sdm630", - "qcom,mdss_dp_pll_sdm630" + "qcom,mdss_hdmi_pll_8998", "qcom,mdss_hdmi_pll_8998_1p8", + "qcom,mdss_dsi_pll_sdm660", "qcom,mdss_dp_pll_sdm660", + "qcom,mdss_dsi_pll_sdm630", "qcom,mdss_dp_pll_sdm630" + - cell-index: Specifies the controller used - reg: offset and length of the register set for the device. - reg-names : names to refer to register sets related to this device diff --git a/Documentation/devicetree/bindings/regulator/rpm-smd-regulator.txt b/Documentation/devicetree/bindings/regulator/rpm-smd-regulator.txt index 1eb27f4c1c56..559ee5b6fc08 100644 --- a/Documentation/devicetree/bindings/regulator/rpm-smd-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/rpm-smd-regulator.txt @@ -104,6 +104,12 @@ Optional properties: parameter. Only one pin may be specified per regulator. This property only applies to BoB type regulators. +- qcom,pwm-threshold-current: Minimum current in mA which requires regulator + to be in PWM mode. Load currents below this + threshold use AUTO mode. This property only + applies to BoB and SMPS type regulators. + If this property is not specified, then the + hardware default mode will be used all the time. - qcom,always-send-voltage: Flag which indicates that updates to the voltage, voltage corner or voltage level set point should always be sent immediately to the diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi index 33bd86654363..06935e80e07b 100644 --- a/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi @@ -157,6 +157,7 @@ <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 349 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 350 IRQ_TYPE_LEVEL_HIGH>; + qcom,deferred-regulator-disable-delay = <80>; vdd-supply = <&gdsc_gpu_cx>; clocks = <&clock_gcc GCC_GPU_CFG_AHB_CLK>, <&clock_gcc GCC_BIMC_GFX_CLK>, diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi index 7de4fbbbf9ff..b53ff87d5092 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi @@ -152,6 +152,10 @@ qcom,mdss-dsi-panel-timings = [00 35 0a 0c 15 1b 09 0d 0a 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x26>; + qcom,mdss-dsi-min-refresh-rate = <55>; + qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; }; &dsi_sharp_4k_dsc_cmd { diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index b7651aee5a67..5708fce44378 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -387,6 +387,8 @@ qcom,mdss-mdp = <&mdss_mdp>; qcom,mdss-fb-map = <&mdss_fb0>; + qcom,null-insertion-enabled; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, <&clock_mmss clk_mmss_mdss_pclk0_clk>, <&clock_mmss clk_mmss_mdss_esc0_clk>, @@ -425,6 +427,8 @@ qcom,mdss-mdp = <&mdss_mdp>; qcom,mdss-fb-map = <&mdss_fb0>; + qcom,null-insertion-enabled; + clocks = <&clock_mmss clk_mmss_mdss_byte1_clk>, <&clock_mmss clk_mmss_mdss_pclk1_clk>, <&clock_mmss clk_mmss_mdss_esc1_clk>, diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi index 3c76519acdcf..7809b81fcefd 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi @@ -87,8 +87,8 @@ qcom,platform-te-gpio = <&tlmm 10 0>; qcom,platform-enable-gpio = <&tlmm 52 0>; qcom,platform-reset-gpio = <&tlmm 94 0>; - qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>; - qcom,platform-bklight-en-gpio-invert; + qcom,platform-avdd-en-gpio = <&pmi8998_gpios 1 0>; + qcom,platform-avdd-en-gpio-invert; }; &mdss_dsi1 { @@ -99,8 +99,8 @@ qcom,platform-te-gpio = <&tlmm 10 0>; qcom,platform-enable-gpio = <&tlmm 52 0>; qcom,platform-reset-gpio = <&tlmm 94 0>; - qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>; - qcom,platform-bklight-en-gpio-invert; + qcom,platform-avdd-en-gpio = <&pmi8998_gpios 1 0>; + qcom,platform-avdd-en-gpio-invert; }; &pmi8998_wled { @@ -114,7 +114,7 @@ }; &pmi8998_gpios { - /* GPIO 1 for WLED power enable */ + /* GPIO 1 for AVDD power enable */ gpio@c000 { qcom,mode = <1>; qcom,output-type = <0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi index 0d0c66d7f26e..9b1145242cc0 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi @@ -420,8 +420,8 @@ qcom,platform-te-gpio = <&tlmm 10 0>; qcom,platform-enable-gpio = <&tlmm 52 0>; qcom,platform-reset-gpio = <&tlmm 94 0>; - qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>; - qcom,platform-bklight-en-gpio-invert; + qcom,platform-avdd-en-gpio = <&pmi8998_gpios 1 0>; + qcom,platform-avdd-en-gpio-invert; }; &mdss_dsi1 { @@ -432,8 +432,8 @@ qcom,platform-te-gpio = <&tlmm 10 0>; qcom,platform-enable-gpio = <&tlmm 52 0>; qcom,platform-reset-gpio = <&tlmm 94 0>; - qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>; - qcom,platform-bklight-en-gpio-invert; + qcom,platform-avdd-en-gpio = <&pmi8998_gpios 1 0>; + qcom,platform-avdd-en-gpio-invert; }; &pmi8998_wled { @@ -447,7 +447,7 @@ }; &pmi8998_gpios { - /* GPIO 1 for WLED power enable */ + /* GPIO 1 for AVDD power enable */ gpio@c000 { qcom,mode = <1>; qcom,output-type = <0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi index be70f129e272..bfd2a035d2a4 100644 --- a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi @@ -502,6 +502,8 @@ pmi8998_bob: regulator-bob { regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; status = "okay"; }; pmi8998_bob_pin1: regulator-bob-pin1 { @@ -510,6 +512,8 @@ qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage1; }; pmi8998_bob_pin2: regulator-bob-pin2 { @@ -518,6 +522,8 @@ qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage2; }; pmi8998_bob_pin3: regulator-bob-pin3 { @@ -526,6 +532,8 @@ qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; + qcom,pwm-threshold-current = <2000000>; + qcom,init-bob-mode = <2>; qcom,use-pin-ctrl-voltage3; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi index 6098a96db206..6e19c77995c1 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi @@ -34,6 +34,40 @@ qcom,msm_ext_disp = <&msm_ext_disp>; }; + sde_hdmi_cec: qcom,hdmi-cec@c9a0000 { + compatible = "qcom,hdmi-cec"; + label = "sde_hdmi_cec"; + qcom,hdmi-dev = <&sde_hdmi>; + interrupt-parent = <&sde_hdmi_tx>; + interrupts = <1 0>; + + reg = <0xc9a0000 0x50c>; + reg-names = "hdmi_cec"; + + clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_mmss clk_mmss_mdss_hdmi_clk>; + clock-names = "cec_mnoc_clk", "cec_iface_clk", "cec_core_clk"; + + pinctrl-names = "cec_active", "cec_sleep"; + pinctrl-0 = <&mdss_hdmi_cec_active>; + pinctrl-1 = <&mdss_hdmi_cec_suspend>; + + cec-gdsc-supply = <&gdsc_mdss>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "cec-gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; }; &sde_kms { diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index f7dbda515643..795635d8d13d 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -175,6 +175,8 @@ reg-names = "core_physical", "qfprom_physical", "hdcp_physical"; interrupt-parent = <&sde_kms>; interrupts = <8 0>; + interrupt-controller; + #interrupt-cells = <1>; qcom,hdmi-tx-ddc-clk-gpio = <&tlmm 32 0>; qcom,hdmi-tx-ddc-data-gpio = <&tlmm 33 0>; qcom,hdmi-tx-hpd-gpio = <&tlmm 34 0>; @@ -182,11 +184,9 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mdss_hdmi_hpd_active &mdss_hdmi_ddc_active - &mdss_hdmi_cec_active &mdss_hdmi_5v_active>; pinctrl-1 = <&mdss_hdmi_hpd_suspend &mdss_hdmi_ddc_suspend - &mdss_hdmi_cec_suspend &mdss_hdmi_5v_suspend>; hpd-gdsc-supply = <&gdsc_mdss>; qcom,supply-names = "hpd-gdsc"; diff --git a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi index 75b30791ffdc..834a5706a525 100644 --- a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi @@ -395,6 +395,7 @@ reset-names = "micro_iface_reset"; qcom,src-clock-rates = <120000000 256000000 384000000 480000000 540000000 576000000>; + qcom,micro-reset; qcom,cpp-fw-payload-info { qcom,stripe-base = <790>; qcom,plane-base = <715>; diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index baced7758c9f..f75794ba942f 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -267,8 +267,10 @@ qusb_phy0: qusb@c012000 { compatible = "qcom,qusb2phy"; reg = <0x0c012000 0x180>, + <0x01fcb24c 0x4>, <0x00188018 0x4>; reg-names = "qusb_phy_base", + "tcsr_clamp_dig_n_1p8", "ref_clk_addr"; vdd-supply = <&pm660l_l1>; vdda18-supply = <&pm660_l10>; diff --git a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig index 953d700c20cc..b359c78a0d29 100644 --- a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig @@ -405,6 +405,7 @@ CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_SDE_HDMI_CEC=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index d81c10d11318..994b0f230968 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -407,6 +407,7 @@ CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_SDE_HDMI_CEC=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c index 59245ba320f6..99c18e3d66d7 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, 2013-2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -682,7 +682,7 @@ int bt_register_slimdev(struct device *dev) static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int ret, pwr_cntrl = 0; + int ret = 0, pwr_cntrl = 0; switch (cmd) { case BT_CMD_SLIM_TEST: diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 05da1fb1f975..1faebb1759e2 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -296,9 +296,9 @@ static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { - int i, ret = -EINVAL, *slot, j = 0, num = 1; + int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1; struct btfmslim *btfmslim = dai->dev->platform_data; - struct btfmslim_ch *ch; + struct btfmslim_ch *ch = NULL; if (!btfmslim) return ret; diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 382717bad828..21994d53db91 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -28,7 +28,8 @@ #define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7))) #define diag_check_update(x) \ - (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \ + (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x))) \ + || (info && (info->peripheral_mask & MD_PERIPHERAL_PD_MASK(x)))) \ struct diag_mask_info msg_mask; struct diag_mask_info msg_bt_mask; @@ -89,7 +90,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) int err = 0; int send_once = 0; int header_len = sizeof(struct diag_ctrl_log_mask); - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_ctrl_log_mask ctrl_pkt; @@ -106,11 +107,25 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) return; } - if (driver->md_session_mask != 0 && - driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) - mask_info = driver->md_session_map[peripheral]->log_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->log_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->log_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &log_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -195,7 +210,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) static void diag_send_event_mask_update(uint8_t peripheral) { - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; struct diag_ctrl_event_mask header; struct diag_mask_info *mask_info = NULL; @@ -220,11 +235,25 @@ static void diag_send_event_mask_update(uint8_t peripheral) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->event_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->event_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->event_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &event_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -284,7 +313,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) int err = 0; int header_len = sizeof(struct diag_ctrl_msg_mask); int temp_len = 0; - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_mask_info *mask_info = NULL; @@ -301,11 +330,25 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->msg_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->msg_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->msg_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &msg_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index a5d92c51cc0b..7b414bd7d808 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -360,10 +360,6 @@ drop_data: err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int)); - } else { - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag: md_session_map[%d] with pid = %d Exited..\n", - peripheral, driver->md_session_map[peripheral]->pid); } diag_ws_on_copy_complete(DIAG_WS_MUX); if (drain_again) diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 73296b573436..a7d7fd176302 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -235,6 +235,10 @@ #define MD_PERIPHERAL_MASK(x) (1 << x) +#define MD_PERIPHERAL_PD_MASK(x) \ + ((x == PERIPHERAL_MODEM) ? (1 << UPD_WLAN) : \ + ((x == PERIPHERAL_LPASS) ? (1 << UPD_AUDIO | 1 << UPD_SENSORS) : 0))\ + /* * Number of stm processors includes all the peripherals and * apps.Added 1 below to indicate apps @@ -675,6 +679,7 @@ void diag_cmd_remove_reg_by_proc(int proc); int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry); int diag_mask_param(void); void diag_clear_masks(struct diag_md_session_t *info); +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask); void diag_record_stats(int type, int flag); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 5c1094b48e92..6be7c48f75a8 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -403,6 +403,30 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask) ret |= DIAG_CON_UPD_SENSORS; return ret; } + +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask) +{ + uint8_t upd = 0; + uint32_t pd_mask = 0; + + pd_mask = diag_translate_kernel_to_user_mask(peripheral_mask); + switch (pd_mask) { + case DIAG_CON_UPD_WLAN: + upd = UPD_WLAN; + break; + case DIAG_CON_UPD_AUDIO: + upd = UPD_AUDIO; + break; + case DIAG_CON_UPD_SENSORS: + upd = UPD_SENSORS; + break; + default: + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with no pd mask set\n"); + } + return upd; +} + int diag_mask_param(void) { return diag_mask_clear_param; diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 9d9aa61c480a..7cc1c56a2090 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -1541,8 +1541,8 @@ static int clk_osm_setup_hw_table(struct clk_osm *c) { struct osm_entry *entry = c->osm_table; int i; - u32 freq_val, volt_val, override_val, spare_val; - u32 table_entry_offset, last_spare, last_virtual_corner = 0; + u32 freq_val = 0, volt_val = 0, override_val = 0, spare_val = 0; + u32 table_entry_offset = 0, last_spare = 0, last_virtual_corner = 0; for (i = 0; i < OSM_TABLE_SIZE; i++) { if (i < c->num_entries) { @@ -2758,7 +2758,7 @@ static ssize_t debugfs_trace_method_get(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct clk_osm *c = file->private_data; - int len, rc; + int len = 0, rc; if (IS_ERR(file) || file == NULL) { pr_err("input error %ld\n", PTR_ERR(file)); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c index c60c4864442f..c4215f30acce 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,10 @@ #define _W(x, y, z) MDSS_PLL_REG_W(x, y, z) #define _R(x, y) MDSS_PLL_REG_R(x, y) +/* CONSTANTS */ +#define HDMI_VERSION_8998_3_3 1 +#define HDMI_VERSION_8998_1_8 2 + /* PLL REGISTERS */ #define FREQ_UPDATE (0x008) #define BIAS_EN_CLKBUFLR_EN (0x034) @@ -277,7 +281,7 @@ find_optimal_index: } static int hdmi_8998_config_phy(unsigned long rate, - struct hdmi_8998_reg_cfg *cfg) + struct hdmi_8998_reg_cfg *cfg, u32 ver) { u64 const high_freq_bit_clk_threshold = 3400000000UL; u64 const dig_freq_bit_clk_threshold = 1500000000UL; @@ -359,6 +363,7 @@ static int hdmi_8998_config_phy(unsigned long rate, pr_debug("INTEGLOOP_GAIN = %llu\n", integloop_gain); pr_debug("CMP_RNG = %llu\n", cmp_rng); pr_debug("PLL_CMP = %llu\n", pll_cmp); + pr_debug("VER=%d\n", ver); cfg->svs_mode_clk_sel = (digclk_divsel & 0xFF); cfg->hsclk_sel = (0x20 | hsclk_sel); @@ -382,82 +387,105 @@ static int hdmi_8998_config_phy(unsigned long rate, cfg->core_clk_en = 0x2C; cfg->coreclk_div_mode0 = 0x5; cfg->phy_mode = (tmds_bclk_ratio ? 0x5 : 0x4); + /* V1P8_SEL */ + if (ver == HDMI_VERSION_8998_1_8) + cfg->phy_mode |= 1 << 4; cfg->ssc_en_center = 0x0; - if (bclk > high_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0xA; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0xA; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0xA; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x1C; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x1C; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x1C; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; - } else if (bclk > dig_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0x9; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0x9; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0x9; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x16; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x16; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x16; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; - } else if (bclk > mid_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0x9; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0x9; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0x9; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x0E; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x0E; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x0E; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; + if (ver == HDMI_VERSION_8998_3_3) { + if (bclk > high_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0xA; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0xA; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0xA; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x1C; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x1C; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x1C; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else if (bclk > dig_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0x9; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0x9; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0x9; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x16; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x16; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x16; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else if (bclk > mid_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0x9; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0x9; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0x9; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x0E; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x0E; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x0E; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else { + cfg->l0_tx_drv_lvl = 0x0; + cfg->l0_tx_emp_post1_lvl = 0x0; + cfg->l1_tx_drv_lvl = 0x0; + cfg->l1_tx_emp_post1_lvl = 0x0; + cfg->l2_tx_drv_lvl = 0x0; + cfg->l2_tx_emp_post1_lvl = 0x0; + cfg->l3_tx_drv_lvl = 0x0; + cfg->l3_tx_emp_post1_lvl = 0x0; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x01; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x01; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x01; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } } else { - cfg->l0_tx_drv_lvl = 0x0; - cfg->l0_tx_emp_post1_lvl = 0x0; - cfg->l1_tx_drv_lvl = 0x0; - cfg->l1_tx_emp_post1_lvl = 0x0; - cfg->l2_tx_drv_lvl = 0x0; - cfg->l2_tx_emp_post1_lvl = 0x0; - cfg->l3_tx_drv_lvl = 0x0; + cfg->l0_tx_drv_lvl = 0xF; + cfg->l0_tx_emp_post1_lvl = 0x5; + cfg->l1_tx_drv_lvl = 0xF; + cfg->l1_tx_emp_post1_lvl = 0x2; + cfg->l2_tx_drv_lvl = 0xF; + cfg->l2_tx_emp_post1_lvl = 0x2; + cfg->l3_tx_drv_lvl = 0xF; cfg->l3_tx_emp_post1_lvl = 0x0; cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x01; + cfg->l0_pre_driver_2 = 0x1E; cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x01; + cfg->l1_pre_driver_2 = 0x1E; cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x01; + cfg->l2_pre_driver_2 = 0x1E; cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; + cfg->l3_pre_driver_2 = 0x10; } return rc; } -static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate) +static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate, + u32 ver) { int rc = 0; struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); @@ -465,7 +493,7 @@ static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate) struct hdmi_8998_reg_cfg cfg = {0}; void __iomem *phy = io->phy_base, *pll = io->pll_base; - rc = hdmi_8998_config_phy(rate, &cfg); + rc = hdmi_8998_config_phy(rate, &cfg, ver); if (rc) { pr_err("rate calculation failed\n, rc=%d", rc); return rc; @@ -699,7 +727,7 @@ static int hdmi_8998_vco_get_lock_range(struct clk *c, } static int hdmi_8998_vco_rate_atomic_update(struct clk *c, - unsigned long rate) + unsigned long rate, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -707,7 +735,7 @@ static int hdmi_8998_vco_rate_atomic_update(struct clk *c, struct hdmi_8998_reg_cfg cfg = {0}; int rc = 0; - rc = hdmi_8998_config_phy(rate, &cfg); + rc = hdmi_8998_config_phy(rate, &cfg, ver); if (rc) { pr_err("rate calculation failed\n, rc=%d", rc); goto end; @@ -728,7 +756,7 @@ end: return rc; } -static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate) +static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -767,9 +795,9 @@ static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate) set_power_dwn = 1; if (atomic_update) - rc = hdmi_8998_vco_rate_atomic_update(c, rate); + rc = hdmi_8998_vco_rate_atomic_update(c, rate, ver); else - rc = hdmi_8998_pll_set_clk_rate(c, rate); + rc = hdmi_8998_pll_set_clk_rate(c, rate, ver); if (rc) { pr_err("failed to set clk rate\n"); @@ -806,7 +834,7 @@ static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate) return rrate; } -static int hdmi_8998_vco_prepare(struct clk *c) +static int hdmi_8998_vco_prepare(struct clk *c, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -824,7 +852,7 @@ static int hdmi_8998_vco_prepare(struct clk *c) } if (!vco->rate_set && vco->rate) { - rc = hdmi_8998_pll_set_clk_rate(c, vco->rate); + rc = hdmi_8998_pll_set_clk_rate(c, vco->rate, ver); if (rc) { pr_err("set rate failed, rc=%d\n", rc); goto error; @@ -902,10 +930,38 @@ static enum handoff hdmi_8998_vco_handoff(struct clk *c) return ret; } -static struct clk_ops hdmi_8998_vco_clk_ops = { - .set_rate = hdmi_8998_vco_set_rate, +static int hdmi_8998_3p3_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_3_3); +} + +static int hdmi_8998_1p8_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_1_8); +} + +static int hdmi_8998_3p3_vco_prepare(struct clk *c) +{ + return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_3_3); +} + +static int hdmi_8998_1p8_vco_prepare(struct clk *c) +{ + return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_1_8); +} + +static struct clk_ops hdmi_8998_3p3_vco_clk_ops = { + .set_rate = hdmi_8998_3p3_vco_set_rate, + .round_rate = hdmi_8998_vco_round_rate, + .prepare = hdmi_8998_3p3_vco_prepare, + .unprepare = hdmi_8998_vco_unprepare, + .handoff = hdmi_8998_vco_handoff, +}; + +static struct clk_ops hdmi_8998_1p8_vco_clk_ops = { + .set_rate = hdmi_8998_1p8_vco_set_rate, .round_rate = hdmi_8998_vco_round_rate, - .prepare = hdmi_8998_vco_prepare, + .prepare = hdmi_8998_1p8_vco_prepare, .unprepare = hdmi_8998_vco_unprepare, .handoff = hdmi_8998_vco_handoff, }; @@ -915,7 +971,7 @@ static struct hdmi_pll_vco_clk hdmi_vco_clk = { .max_rate = HDMI_VCO_MAX_RATE_HZ, .c = { .dbg_name = "hdmi_8998_vco_clk", - .ops = &hdmi_8998_vco_clk_ops, + .ops = &hdmi_8998_3p3_vco_clk_ops, CLK_INIT(hdmi_vco_clk.c), }, }; @@ -925,7 +981,7 @@ static struct clk_lookup hdmipllcc_8998[] = { }; int hdmi_8998_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res) + struct mdss_pll_resources *pll_res, u32 ver) { int rc = 0; @@ -936,8 +992,20 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev, hdmi_vco_clk.priv = pll_res; + switch (ver) { + case HDMI_VERSION_8998_3_3: + hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops; + break; + case HDMI_VERSION_8998_1_8: + hdmi_vco_clk.c.ops = &hdmi_8998_1p8_vco_clk_ops; + break; + default: + hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops; + break; + }; + rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8998, - ARRAY_SIZE(hdmipllcc_8998)); + ARRAY_SIZE(hdmipllcc_8998)); if (rc) { pr_err("clock register failed, rc=%d\n", rc); return rc; @@ -945,3 +1013,17 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev, return rc; } + +int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8998_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8998_3_3); +} + +int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8998_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8998_1_8); +} diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll.h b/drivers/clk/msm/mdss/mdss-hdmi-pll.h index 19f9b925644a..9e6a39481286 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll.h +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -45,17 +45,19 @@ int hdmi_20nm_pll_clock_register(struct platform_device *pdev, struct mdss_pll_resources *pll_res); int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); -int hdmi_8998_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); +int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); #endif diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c index 01ce2b1817f2..b5c98774ba92 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -149,7 +149,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, "qcom,mdss_hdmi_pll_8996_v3_1p8")) { pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8; } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998")) { - pll_res->pll_interface_type = MDSS_HDMI_PLL_8998; + pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_3_3; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998_1p8")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_1_8; } else { goto err; } @@ -193,8 +195,11 @@ static int mdss_pll_clock_register(struct platform_device *pdev, case MDSS_HDMI_PLL_8996_V3_1_8: rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res); break; - case MDSS_HDMI_PLL_8998: - rc = hdmi_8998_pll_clock_register(pdev, pll_res); + case MDSS_HDMI_PLL_8998_3_3: + rc = hdmi_8998_3p3_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8998_1_8: + rc = hdmi_8998_1p8_pll_clock_register(pdev, pll_res); break; case MDSS_UNKNOWN_PLL: default: @@ -401,6 +406,7 @@ static const struct of_device_id mdss_pll_dt_match[] = { {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"}, {.compatible = "qcom,mdss_dp_pll_8998"}, {.compatible = "qcom,mdss_hdmi_pll_8998"}, + {.compatible = "qcom,mdss_hdmi_pll_8998_1p8"}, {} }; diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h index 8fffaf30d4ec..0120d71f0daf 100644 --- a/drivers/clk/msm/mdss/mdss-pll.h +++ b/drivers/clk/msm/mdss/mdss-pll.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,7 +37,8 @@ enum { MDSS_HDMI_PLL_8996_V2, MDSS_HDMI_PLL_8996_V3, MDSS_HDMI_PLL_8996_V3_1_8, - MDSS_HDMI_PLL_8998, + MDSS_HDMI_PLL_8998_3_3, + MDSS_HDMI_PLL_8998_1_8, MDSS_UNKNOWN_PLL, }; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 16a7b6816744..8decab2a9cce 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -597,7 +597,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) int idx, i; for (i = 0, idx = 0; idx <= index; i++) { - struct acpi_gpio_info info; + struct acpi_gpio_info info = {0, 0}; struct gpio_desc *desc; desc = acpi_get_gpiod_by_index(adev, NULL, i, &info); diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 04de6fd88f8c..6e4dd62d4ed9 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,8 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h> +#include <linux/rbtree.h> /** * DOC: Overview @@ -73,7 +75,8 @@ * allocations and avoiding too much fragmentation. This means free space * searches are O(num_holes). Given that all the fancy features drm_mm supports * something better would be fairly complex and since gfx thrashing is a fairly - * steep cliff not a real concern. Removing a node again is O(1). + * steep cliff not a real concern. Removing a node again is O(1). With the + * rbtree to track free holes, free hole search becomes O(log(num_holes)). * * drm_mm supports a few features: Alignment and range restrictions can be * supplied. Further more every &drm_mm_node has a color value (which is just an @@ -103,6 +106,98 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags); +#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1) + +INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, + u64, __subtree_last, + START, LAST, static inline, drm_mm_interval_tree) + +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_first(&mm->interval_tree, + start, last); +} +EXPORT_SYMBOL(drm_mm_interval_first); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_next(node, start, last); +} +EXPORT_SYMBOL(drm_mm_interval_next); + +static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, + struct drm_mm_node *node) +{ + struct drm_mm *mm = hole_node->mm; + struct rb_node **link, *rb; + struct drm_mm_node *parent; + + node->__subtree_last = LAST(node); + + if (hole_node->allocated) { + rb = &hole_node->rb; + while (rb) { + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last >= node->__subtree_last) + break; + + parent->__subtree_last = node->__subtree_last; + rb = rb_parent(rb); + } + + rb = &hole_node->rb; + link = &hole_node->rb.rb_right; + } else { + rb = NULL; + link = &mm->interval_tree.rb_node; + } + + while (*link) { + rb = *link; + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last < node->__subtree_last) + parent->__subtree_last = node->__subtree_last; + if (node->start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + rb_link_node(&node->rb, rb, link); + rb_insert_augmented(&node->rb, + &mm->interval_tree, + &drm_mm_interval_tree_augment); +} + +static void +rb_insert_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + struct rb_node **new = &(mm->holes_tree.rb_node); + struct rb_node *parent = NULL; + struct drm_mm_node *cur; + + while (*new) { + parent = *new; + cur = rb_entry(parent, struct drm_mm_node, hole_node); + + if (__drm_mm_hole_node_start(hole_node) + < __drm_mm_hole_node_start(cur)) + new = &parent->rb_left; + else + new = &parent->rb_right; + } + rb_link_node(&hole_node->hole_node, parent, new); + rb_insert_color(&hole_node->hole_node, &mm->holes_tree); +} + +static void rb_erase_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + rb_erase(&hole_node->hole_node, &mm->holes_tree); +} + static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -142,6 +237,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -150,14 +246,16 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -178,39 +276,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { - struct drm_mm_node *hole; u64 end = node->start + node->size; - u64 hole_start; - u64 hole_end; + struct drm_mm_node *hole; + u64 hole_start, hole_end; - BUG_ON(node == NULL); + if (WARN_ON(node->size == 0)) + return -EINVAL; /* Find the relevant hole to add our node to */ - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > node->start || hole_end < end) - continue; + hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, + node->start, ~(u64)0); + if (hole) { + if (hole->start < end) + return -ENOSPC; + } else { + hole = list_entry(&mm->head_node.node_list, + typeof(*hole), node_list); + } - node->mm = mm; - node->allocated = 1; + hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); + if (!hole->hole_follows) + return -ENOSPC; - INIT_LIST_HEAD(&node->hole_stack); - list_add(&node->node_list, &hole->node_list); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + if (hole_start > node->start || hole_end < end) + return -ENOSPC; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del_init(&hole->hole_stack); - } + node->mm = mm; + node->allocated = 1; - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + list_add(&node->node_list, &hole->node_list); - return 0; + drm_mm_interval_tree_add_node(hole, node); + + if (node->start == hole_start) { + hole->hole_follows = 0; + list_del(&hole->hole_stack); + rb_erase_hole_node(hole, mm); } - return -ENOSPC; + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); + node->hole_follows = 1; + } + + return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); @@ -237,6 +350,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node) @@ -289,6 +405,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -297,9 +414,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); @@ -308,6 +426,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -338,6 +457,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags); @@ -377,6 +499,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) BUG_ON(__drm_mm_hole_node_start(node) == __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); + rb_erase_hole_node(node, mm); } else BUG_ON(__drm_mm_hole_node_start(node) != __drm_mm_hole_node_end(node)); @@ -385,9 +508,11 @@ void drm_mm_remove_node(struct drm_mm_node *node) if (!prev_node->hole_follows) { prev_node->hole_follows = 1; list_add(&prev_node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(prev_node, mm); } else list_move(&prev_node->hole_stack, &mm->hole_stack); + drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0; } @@ -410,6 +535,46 @@ static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment) return end >= start + size; } +static struct drm_mm_node *get_first_hole(const struct drm_mm *mm, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + struct rb_node *node = rb_first(&mm->holes_tree); + + return rb_entry(node, struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry((mm)->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry((mm)->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static struct drm_mm_node *get_next_hole(struct drm_mm_node *entry, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + return rb_entry(rb_next(&entry->hole_node), + struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry(entry->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry(entry->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static bool drm_mm_hole_traversal_condition(const struct drm_mm *mm, + struct drm_mm_node *entry, enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) + return entry ? 1 : 0; + else + return (&entry->hole_stack != &(mm)->hole_stack) ? 1 : 0; +} + static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, u64 size, unsigned alignment, @@ -427,9 +592,14 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); @@ -471,9 +641,14 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (adj_start < start) adj_start = start; @@ -514,14 +689,21 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color; + new->__subtree_last = old->__subtree_last; old->allocated = 0; new->allocated = 1; + + if (old->hole_follows) + rb_replace_node(&old->hole_node, &new->hole_node, + &old->mm->holes_tree); + } EXPORT_SYMBOL(drm_mm_replace_node); @@ -746,7 +928,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0; @@ -756,7 +937,10 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->interval_tree = RB_ROOT; mm->color_adjust = NULL; + mm->holes_tree = RB_ROOT; + rb_insert_hole_node(&mm->head_node, mm); } EXPORT_SYMBOL(drm_mm_init); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 5838545468f8..dbc198b00792 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -12,6 +12,8 @@ config DRM_MSM select QCOM_SCM select BACKLIGHT_CLASS_DEVICE select MSM_EXT_DISPLAY + select MMU_NOTIFIER + select INTERVAL_TREE default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 4c70472bd338..acc417737558 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -907,6 +907,17 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) } } +static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display) +{ + struct edid *edid = display->edid_ctrl->edid; + + if (edid) + cec_notifier_set_phys_addr_from_edid(display->notifier, edid); + else + cec_notifier_set_phys_addr(display->notifier, + CEC_PHYS_ADDR_INVALID); +} + static void _sde_hdmi_hotplug_work(struct work_struct *work) { struct sde_hdmi *sde_hdmi = @@ -936,6 +947,7 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) sde_free_edid((void **)&sde_hdmi->edid_ctrl); drm_helper_hpd_irq_event(connector->dev); + _sde_hdmi_cec_update_phys_addr(sde_hdmi); } static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) @@ -1673,10 +1685,28 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display) SDE_ERROR("Invalid params\n"); return -EINVAL; } + return 0; +} + +static int _sde_hdmi_cec_init(struct sde_hdmi *display) +{ + struct platform_device *pdev = display->pdev; + + display->notifier = cec_notifier_get(&pdev->dev); + if (!display->notifier) { + SDE_ERROR("CEC notifier get failed\n"); + return -ENOMEM; + } return 0; } +static void _sde_hdmi_cec_deinit(struct sde_hdmi *display) +{ + cec_notifier_set_phys_addr(display->notifier, CEC_PHYS_ADDR_INVALID); + cec_notifier_put(display->notifier); +} + static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) { int rc = 0; @@ -1715,7 +1745,14 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", display->name, rc); - goto error; + goto ext_error; + } + + rc = _sde_hdmi_cec_init(display); + if (rc) { + SDE_ERROR("[%s]CEC init failed, rc=%d\n", + display->name, rc); + goto ext_error; } display->edid_ctrl = sde_edid_init(); @@ -1723,7 +1760,7 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) SDE_ERROR("[%s]sde edid init failed\n", display->name); rc = -ENOMEM; - goto error; + goto cec_error; } display_ctrl = &display->ctrl; @@ -1732,7 +1769,10 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) mutex_unlock(&display->display_lock); return rc; -error: + +cec_error: + (void)_sde_hdmi_cec_deinit(display); +ext_error: (void)_sde_hdmi_debugfs_deinit(display); debug_error: mutex_unlock(&display->display_lock); @@ -1758,6 +1798,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); (void)sde_edid_deinit((void **)&display->edid_ctrl); + (void)_sde_hdmi_cec_deinit(display); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 54506da4f9b0..dff245dec764 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -23,6 +23,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <media/cec-notifier.h> #include "hdmi.h" #include "sde_edid_parser.h" @@ -87,6 +88,7 @@ struct sde_hdmi_ctrl { * @codec_ready: If audio codec is ready. * @client_notify_pending: If there is client notification pending. * @irq_domain: IRQ domain structure. + * @notifier: CEC notifider to convey physical address information. * @root: Debug fs root entry. */ struct sde_hdmi { @@ -116,6 +118,7 @@ struct sde_hdmi { bool client_notify_pending; struct irq_domain *irq_domain; + struct cec_notifier *notifier; /* DEBUG FS */ struct dentry *root; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 34268aaedfc0..26a0638f7792 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -328,7 +328,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, } reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */ reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); @@ -360,7 +359,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, } else { sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0); reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */ reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b9503564cdd6..74f298d7328d 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -391,6 +391,8 @@ static int msm_load(struct drm_device *dev, unsigned long flags) INIT_LIST_HEAD(&priv->vblank_ctrl.event_list); init_kthread_work(&priv->vblank_ctrl.work, vblank_ctrl_worker); spin_lock_init(&priv->vblank_ctrl.lock); + hash_init(priv->mn_hash); + mutex_init(&priv->mn_lock); drm_mode_config_init(dev); @@ -559,7 +561,8 @@ static struct msm_file_private *setup_pagetable(struct msm_drm_private *priv) return ERR_PTR(-ENOMEM); ctx->aspace = msm_gem_address_space_create_instance( - priv->gpu->aspace->mmu, "gpu", 0x100000000, 0x1ffffffff); + priv->gpu->aspace->mmu, "gpu", 0x100000000ULL, + TASK_SIZE_64 - 1); if (IS_ERR(ctx->aspace)) { int ret = PTR_ERR(ctx->aspace); @@ -1141,6 +1144,20 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data, args->flags, &args->handle); } +static int msm_ioctl_gem_svm_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_svm_new *args = data; + + if (args->flags & ~MSM_BO_FLAGS) { + DRM_ERROR("invalid flags: %08x\n", args->flags); + return -EINVAL; + } + + return msm_gem_svm_new_handle(dev, file, args->hostptr, args->size, + args->flags, &args->handle); +} + static inline ktime_t to_ktime(struct drm_msm_timespec timeout) { return ktime_set(timeout.tv_sec, timeout.tv_nsec); @@ -1193,6 +1210,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, { struct drm_msm_gem_info *args = data; struct drm_gem_object *obj; + struct msm_gem_object *msm_obj; struct msm_file_private *ctx = file->driver_priv; int ret = 0; @@ -1203,10 +1221,10 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!obj) return -ENOENT; + msm_obj = to_msm_bo(obj); if (args->flags & MSM_INFO_IOVA) { struct msm_gem_address_space *aspace = NULL; struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj = to_msm_bo(obj); uint64_t iova; if (msm_obj->flags & MSM_BO_SECURE && priv->gpu) @@ -1223,6 +1241,14 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!ret) args->offset = iova; } else { + if (msm_obj->flags & MSM_BO_SVM) { + /* + * Offset for an SVM object is not needed as they are + * already mmap'ed before the SVM ioctl is invoked. + */ + ret = -EACCES; + goto out; + } args->offset = msm_gem_mmap_offset(obj); } @@ -1700,6 +1726,8 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_GEM_SVM_NEW, msm_ioctl_gem_svm_new, + DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 54a3568ca11f..4083e990b2c8 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -34,6 +34,7 @@ #include <linux/of_graph.h> #include <linux/of_device.h> #include <linux/sde_io_util.h> +#include <linux/hashtable.h> #include <asm/sizes.h> #include <linux/kthread.h> @@ -325,6 +326,11 @@ struct msm_drm_private { unsigned int num_connectors; struct drm_connector *connectors[MAX_CONNECTORS]; + /* hash to store mm_struct to msm_mmu_notifier mappings */ + DECLARE_HASHTABLE(mn_hash, 7); + /* protects mn_hash and the msm_mmu_notifier for the process */ + struct mutex mn_lock; + /* Properties */ struct drm_property *plane_property[PLANE_PROP_COUNT]; struct drm_property *crtc_property[CRTC_PROP_COUNT]; @@ -404,10 +410,15 @@ void msm_update_fence(struct drm_device *dev, uint32_t fence); void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv); + void *priv, bool invalidated); int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags); +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *domain, + uint64_t hostptr, uint64_t size); +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma); void msm_gem_address_space_put(struct msm_gem_address_space *aspace); @@ -471,6 +482,12 @@ struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev, struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt, u32 flags); void msm_gem_sync(struct drm_gem_object *obj, u32 op); +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle); +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags); int msm_framebuffer_prepare(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 4b1a586d474d..6bb29c62378d 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -25,6 +25,129 @@ #include "msm_gpu.h" #include "msm_mmu.h" +static void msm_gem_mn_free(struct kref *refcount) +{ + struct msm_mmu_notifier *msm_mn = container_of(refcount, + struct msm_mmu_notifier, refcount); + + mmu_notifier_unregister(&msm_mn->mn, msm_mn->mm); + hash_del(&msm_mn->node); + + kfree(msm_mn); +} + +static int msm_gem_mn_get(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) + return kref_get_unless_zero(&msm_mn->refcount); + return 0; +} + +static void msm_gem_mn_put(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) { + struct msm_drm_private *msm_dev = msm_mn->msm_dev; + + mutex_lock(&msm_dev->mn_lock); + kref_put(&msm_mn->refcount, msm_gem_mn_free); + mutex_unlock(&msm_dev->mn_lock); + } +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end); + +static const struct mmu_notifier_ops msm_mn_ops = { + .invalidate_range_start = msm_mn_invalidate_range_start, +}; + +static struct msm_mmu_notifier * +msm_gem_mn_find(struct msm_drm_private *msm_dev, struct mm_struct *mm, + struct msm_gem_address_space *aspace) +{ + struct msm_mmu_notifier *msm_mn; + int ret = 0; + + mutex_lock(&msm_dev->mn_lock); + hash_for_each_possible(msm_dev->mn_hash, msm_mn, node, + (unsigned long) mm) { + if (msm_mn->mm == mm) { + if (!msm_gem_mn_get(msm_mn)) { + ret = -EINVAL; + goto fail; + } + mutex_unlock(&msm_dev->mn_lock); + return msm_mn; + } + } + + msm_mn = kzalloc(sizeof(*msm_mn), GFP_KERNEL); + if (!msm_mn) { + ret = -ENOMEM; + goto fail; + } + + msm_mn->mm = current->mm; + msm_mn->mn.ops = &msm_mn_ops; + ret = mmu_notifier_register(&msm_mn->mn, msm_mn->mm); + if (ret) { + kfree(msm_mn); + goto fail; + } + + msm_mn->svm_tree = RB_ROOT; + spin_lock_init(&msm_mn->svm_tree_lock); + kref_init(&msm_mn->refcount); + msm_mn->msm_dev = msm_dev; + + /* Insert the msm_mn into the hash */ + hash_add(msm_dev->mn_hash, &msm_mn->node, (unsigned long) msm_mn->mm); + mutex_unlock(&msm_dev->mn_lock); + + return msm_mn; + +fail: + mutex_unlock(&msm_dev->mn_lock); + return ERR_PTR(ret); +} + +static int msm_gem_mn_register(struct msm_gem_svm_object *msm_svm_obj, + struct msm_gem_address_space *aspace) +{ + struct drm_gem_object *obj = &msm_svm_obj->msm_obj_base.base; + struct msm_drm_private *msm_dev = obj->dev->dev_private; + struct msm_mmu_notifier *msm_mn; + + msm_svm_obj->mm = current->mm; + msm_svm_obj->svm_node.start = msm_svm_obj->hostptr; + msm_svm_obj->svm_node.last = msm_svm_obj->hostptr + obj->size - 1; + + msm_mn = msm_gem_mn_find(msm_dev, msm_svm_obj->mm, aspace); + if (IS_ERR(msm_mn)) + return PTR_ERR(msm_mn); + + msm_svm_obj->msm_mn = msm_mn; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_insert(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); + + return 0; +} + +static void msm_gem_mn_unregister(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_mmu_notifier *msm_mn = msm_svm_obj->msm_mn; + + /* invalid: bo already unregistered */ + if (!msm_mn || msm_svm_obj->invalid) + return; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_remove(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); +} + static int protect_pages(struct msm_gem_object *msm_obj) { int perm = PERM_READ | PERM_WRITE; @@ -175,10 +298,19 @@ static void put_pages(struct drm_gem_object *obj) sg_free_table(msm_obj->sgt); kfree(msm_obj->sgt); - if (use_pages(obj)) - drm_gem_put_pages(obj, msm_obj->pages, true, false); - else + if (use_pages(obj)) { + if (msm_obj->flags & MSM_BO_SVM) { + int npages = obj->size >> PAGE_SHIFT; + + release_pages(msm_obj->pages, npages, 0); + kfree(msm_obj->pages); + } else { + drm_gem_put_pages(obj, msm_obj->pages, + true, false); + } + } else { put_pages_vram(obj); + } msm_obj->pages = NULL; } @@ -205,8 +337,8 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj, { struct msm_gem_object *msm_obj = to_msm_bo(obj); - /* We can't mmap secure objects */ - if (msm_obj->flags & MSM_BO_SECURE) { + /* We can't mmap secure objects or SVM objects */ + if (msm_obj->flags & (MSM_BO_SECURE | MSM_BO_SVM)) { drm_gem_vm_close(vma); return -EACCES; } @@ -348,14 +480,21 @@ static void put_iova(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj; struct msm_gem_vma *domain, *tmp; + bool invalid = false; WARN_ON(!mutex_is_locked(&msm_obj->lock)); + if (msm_obj->flags & MSM_BO_SVM) { + msm_svm_obj = to_msm_svm_obj(msm_obj); + invalid = msm_svm_obj->invalid; + } + list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { if (iommu_present(&platform_bus_type)) { msm_gem_unmap_vma(domain->aspace, domain, - msm_obj->sgt, get_dmabuf_ptr(obj)); + msm_obj->sgt, get_dmabuf_ptr(obj), invalid); } obj_remove_domain(domain); @@ -658,15 +797,26 @@ void msm_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj = NULL; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); /* object should not be on active list: */ WARN_ON(is_active(msm_obj)); + + if (msm_obj->flags & MSM_BO_SVM) + msm_svm_obj = to_msm_svm_obj(msm_obj); + list_del(&msm_obj->mm_list); - mutex_lock(&msm_obj->lock); + /* Unregister SVM object from mmu notifications */ + if (msm_obj->flags & MSM_BO_SVM) { + msm_gem_mn_unregister(msm_svm_obj); + msm_gem_mn_put(msm_svm_obj->msm_mn); + msm_svm_obj->msm_mn = NULL; + } + mutex_lock(&msm_obj->lock); put_iova(obj); if (obj->import_attach) { @@ -691,7 +841,10 @@ void msm_gem_free_object(struct drm_gem_object *obj) drm_gem_object_release(obj); mutex_unlock(&msm_obj->lock); - kfree(msm_obj); + if (msm_obj->flags & MSM_BO_SVM) + kfree(msm_svm_obj); + else + kfree(msm_obj); } /* convenience method to construct a GEM buffer object, and userspace handle */ @@ -714,26 +867,32 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, return ret; } -static inline void msm_gem_add_to_inactive_list(struct msm_gem_object *msm_obj, - struct drm_device *dev, bool struct_mutex_locked) +/* convenience method to construct an SVM buffer object, and userspace handle */ +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle) { - struct msm_drm_private *priv = dev->dev_private; + struct drm_gem_object *obj; + int ret; - if (struct_mutex_locked) { - list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - } else { - mutex_lock(&dev->struct_mutex); - list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - mutex_unlock(&dev->struct_mutex); - } + obj = msm_gem_svm_new(dev, file, hostptr, size, flags); + + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = drm_gem_handle_create(file, obj, handle); + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); + + return ret; } -static int msm_gem_new_impl(struct drm_device *dev, - uint32_t size, uint32_t flags, struct drm_gem_object **obj, - bool struct_mutex_locked) +static int msm_gem_obj_init(struct drm_device *dev, + uint32_t size, uint32_t flags, + struct msm_gem_object *msm_obj, bool struct_mutex_locked) { struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj; bool use_vram = false; switch (flags & MSM_BO_CACHE_MASK) { @@ -755,10 +914,6 @@ static int msm_gem_new_impl(struct drm_device *dev, if (WARN_ON(use_vram && !priv->vram.size)) return -EINVAL; - msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); - if (!msm_obj) - return -ENOMEM; - mutex_init(&msm_obj->lock); if (use_vram) { @@ -773,20 +928,44 @@ static int msm_gem_new_impl(struct drm_device *dev, msm_obj->resv = &msm_obj->_resv; reservation_object_init(msm_obj->resv); + INIT_LIST_HEAD(&msm_obj->mm_list); INIT_LIST_HEAD(&msm_obj->submit_entry); INIT_LIST_HEAD(&msm_obj->domains); - msm_gem_add_to_inactive_list(msm_obj, dev, struct_mutex_locked); - - *obj = &msm_obj->base; + if (struct_mutex_locked) { + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + } else { + mutex_lock(&dev->struct_mutex); + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + mutex_unlock(&dev->struct_mutex); + } return 0; } +static struct drm_gem_object *msm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags, bool struct_mutex_locked) +{ + struct msm_gem_object *msm_obj; + int ret; + + msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); + if (!msm_obj) + return ERR_PTR(-ENOMEM); + + ret = msm_gem_obj_init(dev, size, flags, msm_obj, struct_mutex_locked); + if (ret) { + kfree(msm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags, bool struct_mutex_locked) { - struct drm_gem_object *obj = NULL; + struct drm_gem_object *obj; int ret; size = PAGE_ALIGN(size); @@ -798,9 +977,9 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, if (!size) return ERR_PTR(-EINVAL); - ret = msm_gem_new_impl(dev, size, flags, &obj, struct_mutex_locked); - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, flags, struct_mutex_locked); + if (IS_ERR(obj)) + return obj; if (use_pages(obj)) { ret = drm_gem_object_init(dev, obj, size); @@ -813,8 +992,7 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } @@ -831,6 +1009,143 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, return _msm_gem_new(dev, size, flags, false); } +static struct drm_gem_object *msm_svm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_object *msm_obj; + int ret; + + msm_svm_obj = kzalloc(sizeof(*msm_svm_obj), GFP_KERNEL); + if (!msm_svm_obj) + return ERR_PTR(-ENOMEM); + + msm_obj = &msm_svm_obj->msm_obj_base; + + ret = msm_gem_obj_init(dev, size, flags | MSM_BO_SVM, msm_obj, false); + if (ret) { + kfree(msm_svm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + +/* convenience method to construct an SVM GEM bo, and userspace handle */ +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags) +{ + struct drm_gem_object *obj; + struct msm_file_private *ctx = file->driver_priv; + struct msm_gem_address_space *aspace = ctx->aspace; + struct msm_gem_object *msm_obj; + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_vma *domain = NULL; + struct page **p; + int npages; + int num_pinned = 0; + int write; + int ret; + + /* if we don't have IOMMU, don't bother pretending we can import: */ + if (!iommu_present(&platform_bus_type)) { + dev_err_once(dev->dev, "cannot import without IOMMU\n"); + return ERR_PTR(-EINVAL); + } + + /* hostptr and size must be page-aligned */ + if (offset_in_page(hostptr | size)) + return ERR_PTR(-EINVAL); + + /* Only CPU cached SVM objects are allowed */ + if ((flags & MSM_BO_CACHE_MASK) != MSM_BO_CACHED) + return ERR_PTR(-EINVAL); + + /* Allocate and initialize a new msm_gem_object */ + obj = msm_svm_gem_new_impl(dev, size, flags); + if (IS_ERR(obj)) + return obj; + + drm_gem_private_object_init(dev, obj, size); + + msm_obj = to_msm_bo(obj); + domain = obj_add_domain(&msm_obj->base, aspace); + if (IS_ERR(domain)) { + drm_gem_object_unreference_unlocked(obj); + return ERR_CAST(domain); + } + + /* Reserve iova if not already in use, else fail */ + ret = msm_gem_reserve_iova(aspace, domain, hostptr, size); + if (ret) { + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(ret); + } + + msm_svm_obj = to_msm_svm_obj(msm_obj); + msm_svm_obj->hostptr = hostptr; + msm_svm_obj->invalid = false; + + ret = msm_gem_mn_register(msm_svm_obj, aspace); + if (ret) + goto fail; + + /* + * Get physical pages and map into smmu in the ioctl itself. + * The driver handles iova allocation, physical page allocation and + * SMMU map all in one go. If we break this, then we have to maintain + * state to tell if physical pages allocation/map needs to happen. + * For SVM, iova reservation needs to happen in the ioctl itself, + * so do the rest right here as well. + */ + npages = size >> PAGE_SHIFT; + p = kcalloc(npages, sizeof(struct page *), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto fail; + } + + write = (msm_obj->flags & MSM_BO_GPU_READONLY) ? 0 : 1; + /* This may hold mm->mmap_sem */ + num_pinned = get_user_pages_fast(hostptr, npages, write, p); + if (num_pinned != npages) { + ret = -EINVAL; + goto free_pages; + } + + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); + if (IS_ERR(msm_obj->sgt)) { + ret = PTR_ERR(msm_obj->sgt); + goto free_pages; + } + + msm_obj->pages = p; + + ret = aspace->mmu->funcs->map(aspace->mmu, domain->iova, + msm_obj->sgt, msm_obj->flags, get_dmabuf_ptr(obj)); + if (ret) + goto free_pages; + + kref_get(&aspace->kref); + + return obj; + +free_pages: + release_pages(p, num_pinned, 0); + kfree(p); + +fail: + if (domain) + msm_gem_release_iova(aspace, domain); + + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); + + return ERR_PTR(ret); +} + struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt, u32 flags) { @@ -846,10 +1161,9 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, size = PAGE_ALIGN(size); - ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj, false); - - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, MSM_BO_WC, false); + if (IS_ERR(obj)) + return obj; drm_gem_private_object_init(dev, obj, size); @@ -868,7 +1182,8 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, /* OR the passed in flags */ msm_obj->flags |= flags; - ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages); + ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, + NULL, npages); if (ret) { mutex_unlock(&msm_obj->lock); goto fail; @@ -879,8 +1194,86 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } + +/* Timeout in ms, long enough so we are sure the GPU is hung */ +#define SVM_OBJ_WAIT_TIMEOUT 10000 +static void invalidate_svm_object(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_gem_object *msm_obj = &msm_svm_obj->msm_obj_base; + struct drm_device *dev = msm_obj->base.dev; + struct msm_gem_vma *domain, *tmp; + uint32_t fence; + int ret; + + if (is_active(msm_obj)) { + ktime_t timeout = ktime_add_ms(ktime_get(), + SVM_OBJ_WAIT_TIMEOUT); + + /* Get the most recent fence that touches the object */ + fence = msm_gem_fence(msm_obj, MSM_PREP_READ | MSM_PREP_WRITE); + + /* Wait for the fence to retire */ + ret = msm_wait_fence(dev, fence, &timeout, true); + if (ret) + /* The GPU could be hung! Not much we can do */ + dev_err(dev->dev, "drm: Error (%d) waiting for svm object: 0x%llx", + ret, msm_svm_obj->hostptr); + } + + /* GPU is done, unmap object from SMMU */ + mutex_lock(&msm_obj->lock); + list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { + struct msm_gem_address_space *aspace = domain->aspace; + + if (domain->iova) + aspace->mmu->funcs->unmap(aspace->mmu, + domain->iova, msm_obj->sgt, + get_dmabuf_ptr(&msm_obj->base)); + } + /* Let go of the physical pages */ + put_pages(&msm_obj->base); + mutex_unlock(&msm_obj->lock); +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end) +{ + struct msm_mmu_notifier *msm_mn = + container_of(mn, struct msm_mmu_notifier, mn); + struct interval_tree_node *itn = NULL; + struct msm_gem_svm_object *msm_svm_obj; + struct drm_gem_object *obj; + LIST_HEAD(inv_list); + + if (!msm_gem_mn_get(msm_mn)) + return; + + spin_lock(&msm_mn->svm_tree_lock); + itn = interval_tree_iter_first(&msm_mn->svm_tree, start, end - 1); + while (itn) { + msm_svm_obj = container_of(itn, + struct msm_gem_svm_object, svm_node); + obj = &msm_svm_obj->msm_obj_base.base; + + if (kref_get_unless_zero(&obj->refcount)) + list_add(&msm_svm_obj->lnode, &inv_list); + + itn = interval_tree_iter_next(itn, start, end - 1); + } + spin_unlock(&msm_mn->svm_tree_lock); + + list_for_each_entry(msm_svm_obj, &inv_list, lnode) { + obj = &msm_svm_obj->msm_obj_base.base; + /* Unregister SVM object from mmu notifications */ + msm_gem_mn_unregister(msm_svm_obj); + msm_svm_obj->invalid = true; + invalidate_svm_object(msm_svm_obj); + drm_gem_object_unreference_unlocked(obj); + } + + msm_gem_mn_put(msm_mn); +} diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index eb850952f1f5..04e6c658b5f3 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -20,11 +20,14 @@ #include <linux/kref.h> #include <linux/reservation.h> +#include <linux/mmu_notifier.h> +#include <linux/interval_tree.h> #include "msm_drv.h" /* Additional internal-use only BO flags: */ #define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */ #define MSM_BO_LOCKED 0x20000000 /* Pages have been securely locked */ +#define MSM_BO_SVM 0x40000000 /* bo is SVM */ struct msm_gem_address_space { const char *name; @@ -85,6 +88,32 @@ struct msm_gem_object { }; #define to_msm_bo(x) container_of(x, struct msm_gem_object, base) +struct msm_mmu_notifier { + struct mmu_notifier mn; + struct mm_struct *mm; /* mm_struct owning the mmu notifier mn */ + struct hlist_node node; + struct rb_root svm_tree; /* interval tree holding all svm bos */ + spinlock_t svm_tree_lock; /* Protects svm_tree*/ + struct msm_drm_private *msm_dev; + struct kref refcount; +}; + +struct msm_gem_svm_object { + struct msm_gem_object msm_obj_base; + uint64_t hostptr; + struct mm_struct *mm; /* mm_struct the svm bo belongs to */ + struct interval_tree_node svm_node; + struct msm_mmu_notifier *msm_mn; + struct list_head lnode; + /* bo has been unmapped on CPU, cannot be part of GPU submits */ + bool invalid; +}; + +#define to_msm_svm_obj(x) \ + ((struct msm_gem_svm_object *) \ + container_of(x, struct msm_gem_svm_object, msm_obj_base)) + + static inline bool is_active(struct msm_gem_object *msm_obj) { return msm_obj->gpu != NULL; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index c9b3151180dc..c861bfd77537 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -217,6 +217,18 @@ retry: submit->bos[i].flags |= BO_LOCKED; } + /* + * An invalid SVM object is part of + * this submit's buffer list, fail. + */ + if (msm_obj->flags & MSM_BO_SVM) { + struct msm_gem_svm_object *msm_svm_obj = + to_msm_svm_obj(msm_obj); + if (msm_svm_obj->invalid) { + ret = -EINVAL; + goto fail; + } + } /* if locking succeeded, pin bo: */ ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova); diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 12e3c0f7c101..f399d24019e4 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -88,7 +88,7 @@ static int allocate_iova(struct msm_gem_address_space *aspace, return 0; } ret = drm_mm_insert_node(&aspace->mm, &vma->node, - size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_DEFAULT); + size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_BOTTOM_UP); spin_unlock(&aspace->lock); @@ -98,6 +98,45 @@ static int allocate_iova(struct msm_gem_address_space *aspace, return ret; } +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, + uint64_t hostptr, uint64_t size) +{ + struct drm_mm *mm = &aspace->mm; + uint64_t start = hostptr >> PAGE_SHIFT; + uint64_t last = (hostptr + size - 1) >> PAGE_SHIFT; + int ret; + + spin_lock(&aspace->lock); + + if (drm_mm_interval_first(mm, start, last)) { + /* iova already in use, fail */ + spin_unlock(&aspace->lock); + return -EADDRINUSE; + } + + vma->node.start = hostptr >> PAGE_SHIFT; + vma->node.size = size >> PAGE_SHIFT; + vma->node.color = 0; + + ret = drm_mm_reserve_node(mm, &vma->node); + if (!ret) + vma->iova = hostptr; + + spin_unlock(&aspace->lock); + + return ret; +} + +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma) +{ + spin_lock(&aspace->lock); + if (drm_mm_node_allocated(&vma->node)) + drm_mm_remove_node(&vma->node); + spin_unlock(&aspace->lock); +} + int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags) @@ -116,11 +155,7 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, flags, priv); if (ret) { - spin_lock(&aspace->lock); - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); - spin_unlock(&aspace->lock); - + msm_gem_release_iova(aspace, vma); return ret; } @@ -131,17 +166,16 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, } void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) + struct msm_gem_vma *vma, struct sg_table *sgt, + void *priv, bool invalidated) { if (!aspace || !vma->iova) return; - aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); + if (!invalidated) + aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); - spin_lock(&aspace->lock); - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); - spin_unlock(&aspace->lock); + msm_gem_release_iova(aspace, vma); vma->iova = 0; diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index a89a92d253ac..564d20079715 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -721,7 +721,8 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) #define VIN_FLASH_MIN_UV 3300000LL static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led) { - int ocv_uv, rbatt_uohm, ibat_now, voltage_hdrm_mv, rc; + int ocv_uv = 0, rbatt_uohm = 0, ibat_now = 0, voltage_hdrm_mv = 0; + int rc = 0; int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip; diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 7cef735a01a7..4f6086970131 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -691,7 +691,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *metadata = NULL; int err = 0, i, mode; - char *key_id, *table_ptr, dummy, *target_device, + char *key_id = NULL, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 5d42d8f09421..1a7b11d57256 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2191,8 +2191,11 @@ static void dm_request_fn(struct request_queue *q) tio = tio_from_request(rq); /* Establish tio->ti before queuing work (map_tio_request) */ tio->ti = ti; - queue_kthread_work(&md->kworker, &tio->work); + spin_unlock(q->queue_lock); + if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE) + dm_requeue_original_request(md, rq); BUG_ON(!irqs_disabled()); + spin_lock(q->queue_lock); } goto out; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 0b219a81e8a2..c1b999c6cff2 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -99,6 +99,10 @@ config MEDIA_CEC_DEBUG config MEDIA_CEC_EDID bool +config MEDIA_CEC_NOTIFIER + bool + select MEDIA_CEC_EDID + # # Media controller # Selectable only for webcam/grabbers, as other drivers don't use it diff --git a/drivers/media/Makefile b/drivers/media/Makefile index ba516dcbc6aa..6c7801919b98 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -10,6 +10,10 @@ ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y) obj-$(CONFIG_MEDIA_SUPPORT) += cec/ endif +ifeq ($(CONFIG_MEDIA_CEC_NOTIFIER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += cec-notifier.o +endif + media-objs := media-device.o media-devnode.o media-entity.o # diff --git a/drivers/media/cec-notifier.c b/drivers/media/cec-notifier.c new file mode 100644 index 000000000000..5f5209a73665 --- /dev/null +++ b/drivers/media/cec-notifier.c @@ -0,0 +1,129 @@ +/* + * cec-notifier.c - notify CEC drivers of physical address changes + * + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/export.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/kref.h> + +#include <media/cec-notifier.h> +#include <drm/drm_edid.h> + +struct cec_notifier { + struct mutex lock; + struct list_head head; + struct kref kref; + struct device *dev; + struct cec_adapter *cec_adap; + void (*callback)(struct cec_adapter *adap, u16 pa); + + u16 phys_addr; +}; + +static LIST_HEAD(cec_notifiers); +static DEFINE_MUTEX(cec_notifiers_lock); + +struct cec_notifier *cec_notifier_get(struct device *dev) +{ + struct cec_notifier *n; + + mutex_lock(&cec_notifiers_lock); + list_for_each_entry(n, &cec_notifiers, head) { + if (n->dev == dev) { + kref_get(&n->kref); + mutex_unlock(&cec_notifiers_lock); + return n; + } + } + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) + goto unlock; + n->dev = dev; + n->phys_addr = CEC_PHYS_ADDR_INVALID; + mutex_init(&n->lock); + kref_init(&n->kref); + list_add_tail(&n->head, &cec_notifiers); +unlock: + mutex_unlock(&cec_notifiers_lock); + return n; +} +EXPORT_SYMBOL_GPL(cec_notifier_get); + +static void cec_notifier_release(struct kref *kref) +{ + struct cec_notifier *n = + container_of(kref, struct cec_notifier, kref); + + list_del(&n->head); + kfree(n); +} + +void cec_notifier_put(struct cec_notifier *n) +{ + mutex_lock(&cec_notifiers_lock); + kref_put(&n->kref, cec_notifier_release); + mutex_unlock(&cec_notifiers_lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_put); + +void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) +{ + mutex_lock(&n->lock); + n->phys_addr = pa; + if (n->callback) + n->callback(n->cec_adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr); + +void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid) +{ + u16 pa = CEC_PHYS_ADDR_INVALID; + + if (edid && edid->extensions) + pa = cec_get_edid_phys_addr((const u8 *)edid, + EDID_LENGTH * (edid->extensions + 1), NULL); + cec_notifier_set_phys_addr(n, pa); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid); + +void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)) +{ + kref_get(&n->kref); + mutex_lock(&n->lock); + n->cec_adap = adap; + n->callback = callback; + n->callback(adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_register); + +void cec_notifier_unregister(struct cec_notifier *n) +{ + mutex_lock(&n->lock); + n->callback = NULL; + mutex_unlock(&n->lock); + cec_notifier_put(n); +} +EXPORT_SYMBOL_GPL(cec_notifier_unregister); diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index aca3ab83a8a1..61adc28ec8ec 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -195,6 +195,24 @@ static void cec_devnode_unregister(struct cec_devnode *devnode) put_device(&devnode->dev); } +#ifdef CONFIG_MEDIA_CEC_NOTIFIER +static void cec_cec_notify(struct cec_adapter *adap, u16 pa) +{ + cec_s_phys_addr(adap, pa, false); +} + +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier) +{ + if (WARN_ON(!adap->devnode.registered)) + return; + + adap->notifier = notifier; + cec_notifier_register(adap->notifier, adap, cec_cec_notify); +} +EXPORT_SYMBOL_GPL(cec_register_cec_notifier); +#endif + struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las) @@ -344,6 +362,10 @@ void cec_unregister_adapter(struct cec_adapter *adap) adap->rc = NULL; #endif debugfs_remove_recursive(adap->cec_dir); +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + if (adap->notifier) + cec_notifier_unregister(adap->notifier); +#endif cec_devnode_unregister(&adap->devnode); } EXPORT_SYMBOL_GPL(cec_unregister_adapter); diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 359a860fdabb..b382a1d83d92 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,29 +27,68 @@ #include <media/v4l2-ctrls.h> #include <linux/mutex.h> #include <linux/delay.h> + +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> + #include <media/adv7481.h> #include <media/msm_ba.h> #include "adv7481_reg.h" +#include "msm_cci.h" +#include "msm_camera_i2c.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" + #define DRIVER_NAME "adv7481" -#define I2C_RW_DELAY 100 -#define I2C_SW_RST_DELAY 10000 -#define GPIO_HW_DELAY_LOW 100000 -#define GPIO_HW_DELAY_HI 10000 +#define I2C_RW_DELAY 1 +#define I2C_SW_RST_DELAY 5000 +#define GPIO_HW_RST_DELAY_HI 10000 +#define GPIO_HW_RST_DELAY_LOW 10000 #define SDP_MIN_SLEEP 5000 #define SDP_MAX_SLEEP 6000 #define SDP_NUM_TRIES 30 #define LOCK_MIN_SLEEP 5000 #define LOCK_MAX_SLEEP 6000 -#define LOCK_NUM_TRIES 20 +#define LOCK_NUM_TRIES 200 + +#define MAX_DEFAULT_WIDTH 1280 +#define MAX_DEFAULT_HEIGHT 720 +#define MAX_DEFAULT_FRAME_RATE 60 +#define MAX_DEFAULT_PIX_CLK_HZ 74240000 #define ONE_MHZ_TO_HZ 1000000 +#define I2C_BLOCK_WRITE_SIZE 1024 + +enum adv7481_gpio_t { + + CCI_I2C_SDA = 0, + CCI_I2C_SCL, + + ADV7481_GPIO_RST, + + ADV7481_GPIO_INT1, + ADV7481_GPIO_INT2, + ADV7481_GPIO_INT3, + + ADV7481_GPIO_MAX, +}; struct adv7481_state { - /* Platform Data */ - struct adv7481_platform_data pdata; + struct device *dev; + + /* VREG */ + struct camera_vreg_t *cci_vreg; + struct regulator *cci_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; + + /* I2C */ + struct msm_camera_i2c_client i2c_client; + u32 cci_master; + u32 i2c_slave_addr; /* V4L2 Data */ struct v4l2_subdev sd; @@ -63,19 +102,25 @@ struct adv7481_state { struct workqueue_struct *work_queues; struct mutex mutex; - struct i2c_client *client; - struct i2c_client *i2c_csi_txa; - struct i2c_client *i2c_csi_txb; - struct i2c_client *i2c_hdmi; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_cp; - struct i2c_client *i2c_sdp; - struct i2c_client *i2c_rep; + uint8_t i2c_io_addr; + uint8_t i2c_csi_txa_addr; + uint8_t i2c_csi_txb_addr; + uint8_t i2c_hdmi_addr; + uint8_t i2c_edid_addr; + uint8_t i2c_cp_addr; + uint8_t i2c_sdp_addr; + uint8_t i2c_rep_addr; + uint8_t i2c_cbus_addr; /* device status and Flags */ int irq; int device_num; int powerup; + int cec_detected; + int clocks_requested; + + /* GPIOs */ + struct gpio gpio_array[ADV7481_GPIO_MAX]; /* routing configuration data */ int csia_src; @@ -196,6 +241,9 @@ const uint8_t adv7481_default_edid_data[] = { #define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data) +static u32 adv7481_inp_to_ba(u32 adv_input); +static bool adv7481_is_timing_locked(struct adv7481_state *state); + static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) { return &(container_of(ctrl->handler, @@ -208,56 +256,151 @@ static inline struct adv7481_state *to_state(struct v4l2_subdev *sd) } /* I2C Rd/Rw Functions */ -static int adv7481_wr_byte(struct i2c_client *i2c_client, unsigned int reg, - unsigned int value) +static int32_t adv7481_cci_i2c_write(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) { - int ret; + return i2c_client->i2c_func_tbl->i2c_write(i2c_client, reg, + *data, data_type); +} - ret = i2c_smbus_write_byte_data(i2c_client, reg & 0xFF, value); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); +static int32_t adv7481_cci_i2c_write_seq( + struct msm_camera_i2c_client *i2c_client, + uint8_t reg, const uint8_t *data, uint32_t size) +{ + return i2c_client->i2c_func_tbl->i2c_write_seq(i2c_client, reg, + (uint8_t *)data, size); +} + +static int32_t adv7481_cci_i2c_read(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) +{ + return i2c_client->i2c_func_tbl->i2c_read(i2c_client, reg, + data, data_type); +} + +static int32_t adv7481_wr_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, uint8_t data) +{ + uint16_t write_data = data; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write(c_i2c_client, reg, &write_data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) + pr_err("Error %d writing cci i2c\n", ret); return ret; } -static int adv7481_rd_byte(struct i2c_client *i2c_client, unsigned int reg) +static int32_t adv7481_wr_block(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, const uint8_t *data, uint32_t size) { - int ret; + int ret = 0; - ret = i2c_smbus_read_byte_data(i2c_client, reg & 0xFF); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write_seq(c_i2c_client, reg, data, size); + if (ret < 0) + pr_err("Error %d writing cci i2c block data\n", ret); return ret; } +static uint8_t adv7481_rd_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return (uint8_t)(data & 0xFF); +} + +static uint16_t adv7481_rd_word(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_WORD_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return data; +} + static int adv7481_set_irq(struct adv7481_state *state) { int ret = 0; - ret = adv7481_wr_byte(state->client, IO_REG_PAD_CTRL_1_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PAD_CTRL_1_ADDR, ADV_REG_SETFIELD(1, IO_PDN_INT2) | ADV_REG_SETFIELD(1, IO_PDN_INT3) | ADV_REG_SETFIELD(1, IO_INV_LLC) | ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT1_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT1_CONF_ADDR, ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR, IO_INTRQ_DUR_SEL) | ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT2_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT2_CONF_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_UNLOCK_EDGE_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_DATAPATH_INT_MASKB_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_CP_UNLOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_VMUTE_REQUEST_HDMI_MB1) | ADV_REG_SETFIELD(1, IO_INT_SD_MB1)); - /* Set hpa */ - ret |= adv7481_wr_byte(state->client, IO_HDMI_LVL_INT_MASKB_3_ADDR, - ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1)); + /* Set cable detect */ + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, + ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1) | + ADV_REG_SETFIELD(1, IO_V_LOCKED_MB1) | + ADV_REG_SETFIELD(1, IO_DE_REGEN_LCK_MB1)); + + /* set CVBS lock/unlock interrupts */ + /* Select SDP MAP 1 */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x20); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_MASK_ADDR, 0x03); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (ret) pr_err("%s: Failed %d to setup interrupt regs\n", __func__, ret); - else - enable_irq(state->irq); + + return ret; +} + +static int adv7481_reset_irq(struct adv7481_state *state) +{ + int ret = 0; + + disable_irq(state->irq); + + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, 0x00); return ret; } @@ -267,27 +410,42 @@ static int adv7481_set_edid(struct adv7481_state *state) int i; int ret = 0; uint8_t edid_state; + uint32_t data_left = 0; + uint32_t start_pos; /* Enable Manual Control of EDID on Port A */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x74, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x74, + 0x01); /* Disable Auto Enable of EDID */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x7A, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x7A, + 0x08); /* Set Primary EDID Size to 256 Bytes */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x70, 0x20); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x70, + 0x20); /* * Readback EDID enable state after a combination of manual * and automatic functions */ - edid_state = adv7481_rd_byte(state->i2c_rep, + edid_state = adv7481_rd_byte(&state->i2c_client, state->i2c_rep_addr, HDMI_REG_RO_EDID_DEBUG_2_ADDR); pr_debug("%s: Readback EDID enable state: 0x%x\n", __func__, edid_state); - for (i = 0; i < ADV7481_EDID_SIZE; i++) { - ret |= adv7481_wr_byte(state->i2c_edid, i, - adv7481_default_edid_data[i]); - } + for (i = 0; i < ADV7481_EDID_SIZE && !ret; i += I2C_BLOCK_WRITE_SIZE) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + i, &adv7481_default_edid_data[i], + I2C_BLOCK_WRITE_SIZE); + + data_left = ADV7481_EDID_SIZE % I2C_BLOCK_WRITE_SIZE; + start_pos = ADV7481_EDID_SIZE - data_left; + if (data_left && !ret) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + start_pos, + &adv7481_default_edid_data[start_pos], + data_left); return ret; } @@ -301,47 +459,232 @@ static irqreturn_t adv7481_irq(int irq, void *dev) return IRQ_HANDLED; } +/* Request CCI clocks for adv7481 register access */ +static int adv7481_request_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == TRUE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + /* enable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) + pr_err("%s:cci enable_vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + +static int adv7481_release_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == FALSE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_RELEASE); + if (ret < 0) + pr_err("%s - cci_release failed\n", __func__); + else + state->clocks_requested = FALSE; + + /* disable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 0); + if (ret < 0) + pr_err("%s:cci disable vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + static void adv7481_irq_delay_work(struct work_struct *work) { struct adv7481_state *state; - uint8_t status; + uint8_t int_raw_status; + uint8_t int_status; + uint8_t raw_status; state = container_of(work, struct adv7481_state, irq_delayed_work.work); mutex_lock(&state->mutex); - /* workaround for irq trigger */ - status = adv7481_rd_byte(state->client, + /* Read raw irq status register */ + int_raw_status = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_INT_RAW_STATUS_ADDR); pr_debug("%s: dev: %d got int raw status: 0x%x\n", __func__, - state->device_num, status); + state->device_num, int_raw_status); + state->cec_detected = ADV_REG_GETFIELD(int_raw_status, IO_INT_CEC_ST); + + while (int_raw_status) { + if (ADV_REG_GETFIELD(int_raw_status, IO_INTRQ1_RAW)) { + int lock_status = -1; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; + + pr_debug("%s: dev: %d got intrq1_raw\n", __func__, + state->device_num); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_INT_STATUS_ADDR); + + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_RAW_STATUS_ADDR); + + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_CLEAR_ADDR, int_status); + + pr_debug("%s: dev: %d got datapath int status: 0x%x\n", + __func__, state->device_num, int_status); + + pr_debug("%s: dev: %d got datapath raw status: 0x%x\n", + __func__, state->device_num, raw_status); + + if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) && + ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) { + uint8_t sdp_sts = 0; + + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x01); + sdp_sts = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RO_MAIN_STATUS1_ADDR); + pr_debug("%s: dev: %d got sdp status: 0x%x\n", + __func__, state->device_num, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + if (ADV_REG_GETFIELD(sdp_sts, + SDP_RO_MAIN_IN_LOCK)) { + lock_status = 0; + pr_debug( + "%s: set lock_status SDP_IN_LOCK:0x%x\n", + __func__, lock_status); + } else { + lock_status = 1; + pr_debug( + "%s: set lock_status SDP_UNLOCK:0x%x\n", + __func__, lock_status); + } + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x20); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_CLR_ADDR, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + } else { + if (ADV_REG_GETFIELD(int_status, + IO_CP_LOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_LOCK_CP_RAW)) { + lock_status = 0; + pr_debug( + "%s: set lock_status IO_CP_LOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + if (ADV_REG_GETFIELD(int_status, + IO_CP_UNLOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_UNLOCK_CP_RAW)) { + lock_status = 1; + pr_debug( + "%s: set lock_status IO_CP_UNLOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + } + + if (lock_status >= 0) { + ptr[0] = adv7481_inp_to_ba(state->mode); + ptr[1] = lock_status; + event.type = lock_status ? + V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK : + V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_INT_STATUS_ADDR); + if (ADV_REG_GETFIELD(int_raw_status, IO_INT_HDMI_ST)) { + int cable_detected = 0; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; - pr_debug("%s: dev: %d got datapath int status: 0x%x\n", __func__, - state->device_num, status); + ptr[0] = adv7481_inp_to_ba(state->mode); - adv7481_wr_byte(state->client, - IO_REG_DATAPATH_INT_CLEAR_ADDR, status); + pr_debug("%s: dev: %d got int_hdmi_st\n", __func__, + state->device_num); - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_RAW_STATUS_ADDR); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_INT_STATUS_3_ADDR); - pr_debug("%s: dev: %d got datapath rawstatus: 0x%x\n", __func__, - state->device_num, status); + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); - status = adv7481_rd_byte(state->client, - IO_HDMI_LVL_INT_STATUS_3_ADDR); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_CLEAR_3_ADDR, int_status); - pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", __func__, - state->device_num, status); + pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", + __func__, state->device_num, int_status); + pr_debug("%s: dev: %d got hdmi lvl raw status 3: 0x%x\n", + __func__, state->device_num, raw_status); - adv7481_wr_byte(state->client, - IO_HDMI_LVL_INT_CLEAR_3_ADDR, status); + if (ADV_REG_GETFIELD(int_status, IO_CABLE_DET_A_ST)) { + cable_detected = ADV_REG_GETFIELD(raw_status, + IO_CABLE_DET_A_RAW); + pr_debug("%s: set cable_detected: 0x%x\n", + __func__, cable_detected); + ptr[1] = cable_detected; + event.type = V4L2_EVENT_MSM_BA_CABLE_DETECT; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + /* Assumption is that vertical sync int + * is the last one to come + */ + if (ADV_REG_GETFIELD(int_status, IO_V_LOCKED_ST)) { + if (ADV_REG_GETFIELD(raw_status, + IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_DE_REGEN_LCK_RAW)) { + pr_debug("%s: port settings changed\n", + __func__); + event.type = + V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } + } + int_raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_INT_RAW_STATUS_ADDR); + } mutex_unlock(&state->mutex); } @@ -350,110 +693,100 @@ static int adv7481_cec_wakeup(struct adv7481_state *state, bool enable) uint8_t val; int ret = 0; - val = adv7481_rd_byte(state->client, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR); val = ADV_REG_GETFIELD(val, IO_PROG_XTAL_FREQ_HIGH); if (enable) { /* CEC wake up enabled in power-down mode */ val |= ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } else { /* CEC wake up disabled in power-down mode */ val |= ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } return ret; } /* Initialize adv7481 I2C Settings */ -static int adv7481_dev_init(struct adv7481_state *state, - struct i2c_client *client) +static int adv7481_dev_init(struct adv7481_state *state) { + uint16_t chip_rev_id; int ret; mutex_lock(&state->mutex); /* Soft reset */ - ret = adv7481_wr_byte(state->client, - IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); /* Delay required following I2C reset and I2C transactions */ - usleep_range(I2C_SW_RST_DELAY, I2C_SW_RST_DELAY+1000); + udelay(I2C_SW_RST_DELAY); + + chip_rev_id = adv7481_rd_word(&state->i2c_client, state->i2c_io_addr, + IO_REG_CHIP_REV_ID_1_ADDR); + pr_debug("%s: ADV7481 chip rev id: 0x%x", __func__, chip_rev_id); /* Disable CEC wake up in power-down mode */ ret |= adv7481_cec_wakeup(state, 0); /* Setting Vid_Std to 720x480p60 */ - ret |= adv7481_wr_byte(state->client, - IO_REG_CP_VID_STD_ADDR, 0x4A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_VID_STD_ADDR, 0x4A); /* Configure I2C Maps and I2C Communication Settings */ /* io_reg_f2 I2C Auto Increment */ - ret |= adv7481_wr_byte(state->client, IO_REG_I2C_CFG_ADDR, - IO_REG_I2C_AUTOINC_EN_REG_VALUE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_I2C_CFG_ADDR, IO_REG_I2C_AUTOINC_EN_REG_VALUE); /* DPLL Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_DPLL_ADDR, - IO_REG_DPLL_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DPLL_ADDR, IO_REG_DPLL_SADDR); /* CP Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CP_ADDR, - IO_REG_CP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_ADDR, IO_REG_CP_SADDR); /* HDMI RX Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_ADDR, - IO_REG_HDMI_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_ADDR, IO_REG_HDMI_SADDR); /* EDID Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_EDID_ADDR, - IO_REG_EDID_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_EDID_ADDR, IO_REG_EDID_SADDR); /* HDMI RX Repeater Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_REP_ADDR, - IO_REG_HDMI_REP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_REP_ADDR, IO_REG_HDMI_REP_SADDR); /* HDMI RX Info-frame Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_INF_ADDR, - IO_REG_HDMI_INF_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_INF_ADDR, IO_REG_HDMI_INF_SADDR); /* CBUS Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CBUS_ADDR, - IO_REG_CBUS_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CBUS_ADDR, IO_REG_CBUS_SADDR); /* CEC Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CEC_ADDR, - IO_REG_CEC_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CEC_ADDR, IO_REG_CEC_SADDR); /* SDP Main Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_SDP_ADDR, - IO_REG_SDP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_SDP_ADDR, IO_REG_SDP_SADDR); /* CSI-TXB Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXB_ADDR, - IO_REG_CSI_TXB_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXB_ADDR, IO_REG_CSI_TXB_SADDR); /* CSI-TXA Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXA_ADDR, - IO_REG_CSI_TXA_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXA_ADDR, IO_REG_CSI_TXA_SADDR); if (ret) { pr_err("%s: Failed dev init %d\n", __func__, ret); goto err_exit; } /* Configure i2c clients */ - state->i2c_csi_txa = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXA_SADDR >> 1); - state->i2c_csi_txb = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXB_SADDR >> 1); - state->i2c_cp = i2c_new_dummy(client->adapter, - IO_REG_CP_SADDR >> 1); - state->i2c_hdmi = i2c_new_dummy(client->adapter, - IO_REG_HDMI_SADDR >> 1); - state->i2c_edid = i2c_new_dummy(client->adapter, - IO_REG_EDID_SADDR >> 1); - state->i2c_sdp = i2c_new_dummy(client->adapter, - IO_REG_SDP_SADDR >> 1); - state->i2c_rep = i2c_new_dummy(client->adapter, - IO_REG_HDMI_REP_SADDR >> 1); - - if (!state->i2c_csi_txa || !state->i2c_csi_txb || !state->i2c_cp || - !state->i2c_sdp || !state->i2c_hdmi || !state->i2c_edid || - !state->i2c_rep) { - pr_err("%s: Additional I2C Client Fail\n", __func__); - ret = -EFAULT; - goto err_exit; - } + state->i2c_csi_txa_addr = IO_REG_CSI_TXA_SADDR >> 1; + state->i2c_csi_txb_addr = IO_REG_CSI_TXB_SADDR >> 1; + state->i2c_cp_addr = IO_REG_CP_SADDR >> 1; + state->i2c_hdmi_addr = IO_REG_HDMI_SADDR >> 1; + state->i2c_edid_addr = IO_REG_EDID_SADDR >> 1; + state->i2c_sdp_addr = IO_REG_SDP_SADDR >> 1; + state->i2c_rep_addr = IO_REG_HDMI_REP_SADDR >> 1; + state->i2c_cbus_addr = IO_REG_CBUS_SADDR >> 1; ret = adv7481_set_edid(state); ret |= adv7481_set_irq(state); @@ -465,28 +798,25 @@ err_exit: } /* Initialize adv7481 hardware */ -static int adv7481_hw_init(struct adv7481_platform_data *pdata, - struct adv7481_state *state) +static int adv7481_hw_init(struct adv7481_state *state) { int ret = 0; - if (!pdata) { - pr_err("%s: PDATA is NULL\n", __func__); - return -EFAULT; - } - mutex_lock(&state->mutex); - if (gpio_is_valid(pdata->rstb_gpio)) { - ret = gpio_request(pdata->rstb_gpio, "rstb_gpio"); - if (ret) { - pr_err("%s: Request GPIO Fail %d\n", __func__, ret); - goto err_exit; - } - ret = gpio_direction_output(pdata->rstb_gpio, 0); - usleep_range(GPIO_HW_DELAY_LOW, GPIO_HW_DELAY_LOW+1000); - ret = gpio_direction_output(pdata->rstb_gpio, 1); - usleep_range(GPIO_HW_DELAY_HI, GPIO_HW_DELAY_HI+1000); + /* Bring ADV7481 out of reset */ + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_RST], 1); + if (ret < 0) { + pr_err("%s: Failed to request reset GPIO %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_RST].gpio)) { + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 0); + udelay(GPIO_HW_RST_DELAY_LOW); + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 1); + udelay(GPIO_HW_RST_DELAY_HI); if (ret) { pr_err("%s: Set GPIO Fail %d\n", __func__, ret); goto err_exit; @@ -494,22 +824,21 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, } /* Only setup IRQ1 for now... */ - if (gpio_is_valid(pdata->irq1_gpio)) { - ret = gpio_request(pdata->irq1_gpio, "irq_gpio"); - if (ret) { - pr_err("%s: Failed to request irq_gpio %d\n", - __func__, ret); - goto err_exit; - } - - ret = gpio_direction_input(pdata->irq1_gpio); + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_INT1], 1); + if (ret < 0) { + pr_err("%s: Failed to request irq_gpio %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_INT1].gpio)) { + ret |= gpio_direction_input( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (ret) { pr_err("%s: Failed gpio_direction irq %d\n", __func__, ret); goto err_exit; } - - state->irq = gpio_to_irq(pdata->irq1_gpio); + state->irq = gpio_to_irq( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (state->irq) { ret = request_irq(state->irq, adv7481_irq, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, @@ -519,18 +848,16 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, __func__, ret); goto err_exit; } + /* disable irq until chip interrupts are programmed */ + disable_irq(state->irq); } else { pr_err("%s: Failed gpio_to_irq %d\n", __func__, ret); ret = -EINVAL; goto err_exit; } - - /* disable irq until chip interrupts are programmed */ - disable_irq(state->irq); - - INIT_DELAYED_WORK(&state->irq_delayed_work, - adv7481_irq_delay_work); } + INIT_DELAYED_WORK(&state->irq_delayed_work, + adv7481_irq_delay_work); err_exit: mutex_unlock(&state->mutex); @@ -548,31 +875,40 @@ static int adv7481_s_ctrl(struct v4l2_ctrl *ctrl) pr_debug("Enter %s: id = 0x%x\n", __func__, ctrl->id); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_BRIGHTNESS, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_BRIGHTNESS, ctrl->val); break; case V4L2_CID_CONTRAST: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_CONTRAST, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_CONTRAST, ctrl->val); break; case V4L2_CID_SATURATION: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_SATURATION, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_SATURATION, ctrl->val); break; case V4L2_CID_HUE: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, CP_REG_HUE, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_HUE, ctrl->val); break; default: break; @@ -611,6 +947,108 @@ static int adv7481_s_power(struct v4l2_subdev *sd, int on) return ret; } +static int adv7481_set_cec_logical_addr(struct adv7481_state *state, int *la) +{ + int rc = 0; + uint8_t val; + + if (!la) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOG_ADDR_MASK_ADDR); + if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK0)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS0); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS0); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK1)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS1); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS1); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK2)) { + val = ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS2); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS2_ADDR, val); + } else { + pr_err("No cec logical address mask set\n"); + } + + return rc; +} + +static int adv7481_cec_powerup(struct adv7481_state *state, int *powerup) +{ + int rc = 0; + uint8_t val = 0; + + if (!powerup) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + pr_debug("%s: set power %d\n", __func__, *powerup); + + val = ADV_REG_SETFIELD(*powerup, CEC_REG_CEC_POWER_UP); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_CEC_POWER_UP_ADDR, val); + + return rc; +} + +static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct adv7481_state *state = to_state(sd); + int *ret_val = arg; + long ret = 0; + int param = 0; + + pr_debug("Enter %s with command: 0x%x", __func__, cmd); + + if (!sd) + return -EINVAL; + + switch (cmd) { + case VIDIOC_HDMI_RX_CEC_S_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, arg); + break; + case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, ¶m); + break; + case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: + if (ret_val) { + *ret_val = 0; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_G_CONNECTED: + if (ret_val) { + *ret_val = state->cec_detected; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_S_ENABLE: + ret = adv7481_cec_powerup(state, arg); + break; + default: + pr_err("Not a typewriter! Command: 0x%x", cmd); + ret = -ENOTTY; + break; + } + return ret; +} + static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) { int ret = 0; @@ -620,18 +1058,22 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) if (sd_standard == NULL) return -EINVAL; + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); do { - sdp_stat = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); usleep_range(SDP_MIN_SLEEP, SDP_MAX_SLEEP); timeout++; - sdp_stat2 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); } while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES)); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (sdp_stat != sdp_stat2) { - pr_err("%s(%d), adv7481 SDP status unstable: 1\n", - __func__, __LINE__); + pr_err("%s, adv7481 SDP status unstable: 1\n", __func__); return -ETIMEDOUT; } @@ -681,22 +1123,50 @@ static int adv7481_set_cvbs_mode(struct adv7481_state *state) pr_debug("Enter %s\n", __func__); state->mode = ADV7481_IP_CVBS_1; /* cvbs video settings ntsc etc */ - ret = adv7481_wr_byte(state->client, 0x00, 0x30); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0f, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x00, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x03, 0x42); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x04, 0x07); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x13, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x17, 0x41); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x31, 0x12); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x52, 0xcd); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0xff); - val = adv7481_rd_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x00, 0x30); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0e, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x0f, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x52, 0xcd); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x00, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x80, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x81, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x82, 0x68); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x03, 0x42); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x04, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x13, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x17, 0x41); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x31, 0x12); + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR); /* Output of SD core routed to MIPI CSI 4-lane Tx */ - val |= ADV_REG_SETFIELD(0x10, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, val); - /* Enable autodetect */ - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0x81); + val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); return ret; } @@ -713,69 +1183,101 @@ static int adv7481_set_hdmi_mode(struct adv7481_state *state) * YUV 422 out via TxA CSI: 4-Lane */ /* Disable chip powerdown & Enable HDMI Rx block */ - temp = adv7481_rd_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR); val = ADV_REG_SETFIELD(1, IO_CTRL_RX_EN) | ADV_REG_SETFIELD(0, IO_CTRL_RX_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_XTAL_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_CORE_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_MASTER_PWDN); - ret = adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, val); /* SDR mode */ - ret |= adv7481_wr_byte(state->client, 0x11, 0x48); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x11, 0x48); /* Set CP core to YUV out */ - ret |= adv7481_wr_byte(state->client, 0x04, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x04, 0x00); /* Set CP core to SDR 422 */ - ret |= adv7481_wr_byte(state->client, 0x12, 0xF2); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x12, 0xF2); /* Saturate both Luma and Chroma values to 254 */ - ret |= adv7481_wr_byte(state->client, 0x17, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x17, 0x80); /* Set CP core to enable AV codes */ - ret |= adv7481_wr_byte(state->client, 0x03, 0x86); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x03, 0x86); /* ADI RS CP Core: */ - ret |= adv7481_wr_byte(state->i2c_cp, 0x7C, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_cp_addr, + 0x7C, 0x00); /* Set CP core Phase Adjustment */ - ret |= adv7481_wr_byte(state->client, 0x0C, 0xE0); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0C, 0xE0); /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ - ret |= adv7481_wr_byte(state->client, IO_PAD_CTRLS_ADDR, 0xDD); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_PAD_CTRLS_ADDR, 0xDD); /* Enable Tx A CSI 4-Lane & data from CP core */ val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, - val); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); /* start to configure HDMI Rx once io-map is configured */ /* Enable HDCP 1.1 */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x40, 0x83); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, + 0x40, 0x83); /* Foreground Channel = A */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x00, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x00, 0x08); /* ADI Required Write */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x98, 0xFF); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x99, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9A, 0x00); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9B, 0x0A); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9D, 0x40); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xCB, 0x09); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x98, 0xFF); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x99, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9A, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9B, 0x0A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9D, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xCB, 0x09); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3D, 0x10); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3E, 0x7B); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3F, 0x5E); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4E, 0xFE); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4F, 0x18); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x57, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x58, 0x04); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x85, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3D, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3E, 0x7B); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3F, 0x5E); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4E, 0xFE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4F, 0x18); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x57, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x58, 0x04); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x85, 0x10); /* Enable All Terminations */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x83, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x83, 0x00); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xA3, 0x01); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xBE, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xA3, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xBE, 0x00); /* HPA Manual Enable */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x6C, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x6C, 0x01); /* HPA Asserted */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xF8, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xF8, 0x01); /* Audio Mute Speed Set to Fastest (Smallest Step Size) */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x0F, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x0F, 0x00); return ret; } @@ -860,7 +1362,7 @@ static int adv7481_set_ip_mode(struct adv7481_state *state, int input) } static int adv7481_set_op_src(struct adv7481_state *state, - int output, int input) + int output, int input) { int ret = 0; int temp = 0; @@ -897,10 +1399,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, default: ret = -EINVAL; } - temp = adv7481_rd_byte(state->client, + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DOWN_CTRL_ADDR); temp |= val; - adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, temp); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, temp); state->csia_src = input; break; case ADV7481_OP_CSIB: @@ -915,11 +1418,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, return ret; } -static u32 ba_inp_to_adv7481(u32 input) +static u32 ba_inp_to_adv7481(u32 ba_input) { u32 adv_input = ADV7481_IP_HDMI; - switch (input) { + switch (ba_input) { case BA_IP_CVBS_0: adv_input = ADV7481_IP_CVBS_1; break; @@ -954,6 +1457,42 @@ static u32 ba_inp_to_adv7481(u32 input) return adv_input; } +static u32 adv7481_inp_to_ba(u32 adv_input) +{ + u32 ba_input = BA_IP_HDMI_1; + + switch (adv_input) { + case ADV7481_IP_CVBS_1: + ba_input = BA_IP_CVBS_0; + break; + case ADV7481_IP_CVBS_2: + ba_input = BA_IP_CVBS_1; + break; + case ADV7481_IP_CVBS_3: + ba_input = BA_IP_CVBS_2; + break; + case ADV7481_IP_CVBS_4: + ba_input = BA_IP_CVBS_3; + break; + case ADV7481_IP_CVBS_5: + ba_input = BA_IP_CVBS_4; + break; + case ADV7481_IP_CVBS_6: + ba_input = BA_IP_CVBS_5; + break; + case ADV7481_IP_HDMI: + ba_input = BA_IP_HDMI_1; + break; + case ADV7481_IP_TTL: + ba_input = BA_IP_TTL; + break; + default: + ba_input = BA_IP_HDMI_1; + break; + } + return ba_input; +} + static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -971,14 +1510,11 @@ static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, goto unlock_exit; } - if (state->mode != adv_input) { - ret = adv7481_set_ip_mode(state, adv_input); - if (ret) - pr_err("%s: Set input mode failed: %d\n", - __func__, ret); - else - state->mode = adv_input; - } + ret = adv7481_set_ip_mode(state, adv_input); + if (ret) + pr_err("%s: Set input mode failed: %d\n", __func__, ret); + else + state->mode = adv_input; unlock_exit: mutex_unlock(&state->mutex); @@ -986,6 +1522,27 @@ unlock_exit: return ret; } +static bool adv7481_is_timing_locked(struct adv7481_state *state) +{ + bool ret = false; + int val1 = 0; + int val2 = 0; + + /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + val1 = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); + val2 = adv7481_rd_byte(&state->i2c_client, state->i2c_cp_addr, + CP_REG_STDI_CH_ADDR); + + if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && + ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + ret = true; + + return ret; +} + static int adv7481_get_hdmi_timings(struct adv7481_state *state, struct adv7481_vid_params *vid_params, struct adv7481_hdmi_params *hdmi_params) @@ -998,13 +1555,15 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, pr_debug("Enter %s\n", __func__); /* Check TMDS PLL Lock and Frequency */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM4_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM4_ADDR); hdmi_params->pll_lock = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_PLL_LOCKED); if (hdmi_params->pll_lock) { - temp1 = adv7481_rd_byte(state->i2c_hdmi, - HDMI_REG_TMDS_FREQ_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_FRAC_ADDR); hdmi_params->tmds_freq = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_FREQ); @@ -1015,34 +1574,29 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, hdmi_params->tmds_freq += ADV_REG_GETFIELD(temp2, HDMI_REG_TMDS_FREQ_FRAC)*ONE_MHZ_TO_HZ/128; } else { - pr_err("%s: PLL not locked return EBUSY\n", __func__); - return -EBUSY; + pr_err("%s(%d): PLL not locked return EBUSY\n", + __func__, __LINE__); + ret = -EBUSY; + goto set_default; } - /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + /* Check Timing Lock */ do { - temp1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - temp2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(temp1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(temp1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(temp1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(temp2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), adv7481 HDMI DE regeneration block NOT Locked: 0x%x", - __func__, __LINE__, temp1); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); } /* Check Timing Lock HDMI Map V:0x07[7], H:0x7[5] */ do { - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_LINE_WIDTH_1_ADDR); if (ADV_REG_GETFIELD(temp1, HDMI_VERT_FILTER_LOCKED) && @@ -1059,7 +1613,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, } /* Check HDMI Parameters */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_FIELD1_HEIGHT1_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_FIELD1_HEIGHT1_ADDR); hdmi_params->color_depth = ADV_REG_GETFIELD(temp1, HDMI_REG_DEEP_COLOR_MODE); @@ -1068,22 +1623,25 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_HDMI_INTERLACED); fieldfactor = (vid_params->intrlcd == 1) ? 2 : 1; - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM5_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM5_ADDR); hdmi_params->pix_rep = ADV_REG_GETFIELD(temp1, HDMI_REG_PIXEL_REPETITION); /* Get Active Timing Data HDMI Map H:0x07[4:0] + 0x08[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_2_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_1_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_2_ADDR); vid_params->act_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_LINE_WIDTH_1) << 8) & 0x1F00) | ADV_REG_GETFIELD(temp2, HDMI_REG_LINE_WIDTH_2)); /* Get Total Timing Data HDMI Map H:0x1E[5:0] + 0x1F[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR); vid_params->tot_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_TOTAL_LINE_WIDTH_1) << 8) & 0x3F00) | @@ -1091,9 +1649,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_TOTAL_LINE_WIDTH_2)); /* Get Active Timing Data HDMI Map V:0x09[4:0] + 0x0A[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_2_ADDR); vid_params->act_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_HEIGHT_1) << 8) & 0x1F00) | @@ -1101,9 +1659,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_FIELD0_HEIGHT_2)); /* Get Total Timing Data HDMI Map V:0x26[5:0] + 0x27[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR); vid_params->tot_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_TOT_HEIGHT_1) << 8) & 0x3F00) | @@ -1139,6 +1697,18 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, (hdmi_params->pix_rep + 1)); } +set_default: + if (ret) { + pr_debug("%s(%d), error %d resort to default fmt\n", + __func__, __LINE__, ret); + vid_params->act_pix = MAX_DEFAULT_WIDTH; + vid_params->act_lines = MAX_DEFAULT_HEIGHT; + vid_params->fr_rate = MAX_DEFAULT_FRAME_RATE; + vid_params->pix_clk = MAX_DEFAULT_PIX_CLK_HZ; + vid_params->intrlcd = 0; + ret = 0; + } + pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d @ %d fps\n", __func__, __LINE__, vid_params->act_pix, vid_params->act_lines, @@ -1170,15 +1740,22 @@ static int adv7481_query_dv_timings(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - timings->type = V4L2_DV_BT_656_1120; - bt_timings->width = vid_params.act_pix; - bt_timings->height = vid_params.act_lines; - bt_timings->pixelclock = vid_params.pix_clk; - bt_timings->interlaced = vid_params.intrlcd ? + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + timings->type = V4L2_DV_BT_656_1120; + bt_timings->width = vid_params.act_pix; + bt_timings->height = vid_params.act_lines; + bt_timings->pixelclock = vid_params.pix_clk; + bt_timings->interlaced = vid_params.intrlcd ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - if (bt_timings->interlaced == V4L2_DV_INTERLACED) - bt_timings->height /= 2; + if (bt_timings->interlaced == V4L2_DV_INTERLACED) + bt_timings->height /= 2; + } else { + pr_err( + "%s: Error in adv7481_get_hdmi_timings. ret %d\n", + __func__, ret); + } break; default: return -EINVAL; @@ -1193,10 +1770,24 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) int temp = 0; struct adv7481_state *state = to_state(sd); uint8_t tStatus = 0x0; + uint32_t count = 0; pr_debug("Enter %s\n", __func__); - tStatus = adv7481_rd_byte(state->i2c_sdp, SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + tStatus = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n", __func__, __LINE__, tStatus); @@ -1245,13 +1836,15 @@ static int adv7481_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int adv7481_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { int ret; struct adv7481_vid_params vid_params; struct adv7481_hdmi_params hdmi_params; struct adv7481_state *state = to_state(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; if (!fmt) return -EINVAL; @@ -1267,17 +1860,29 @@ static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - fmt->width = vid_params.act_pix; - fmt->height = vid_params.act_lines; - if (vid_params.intrlcd) - fmt->height /= 2; + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + fmt->width = vid_params.act_pix; + fmt->height = vid_params.act_lines; + if (vid_params.intrlcd) + fmt->height /= 2; + } else { + pr_err("%s: Error %d in adv7481_get_hdmi_timings\n", + __func__, ret); + } + break; + case ADV7481_IP_CVBS_1: + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->width = 720; + fmt->height = 576; break; default: return -EINVAL; } mutex_unlock(&state->mutex); - fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; return ret; } @@ -1290,17 +1895,17 @@ static int adv7481_set_audio_spdif(struct adv7481_state *state, if (on) { /* Configure I2S_SDATA output pin as an SPDIF output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val |= ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } else { /* Configure I2S_SDATA output pin as an I2S output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val &= ~ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } return ret; @@ -1310,40 +1915,42 @@ static int adv7481_csi_powerdown(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* CSI Tx: power down DPHY */ - ret = adv7481_wr_byte(csi_map, CSI_REG_TX_DPHY_PWDN_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_DPHY_PWDN_ADDR, ADV_REG_SETFIELD(1, CSI_CTRL_DPHY_PWDN)); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x00); /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* * ADI Recommended power down sequence * DPHY and CSI Tx A Power down Sequence * CSI TxA: MIPI PLL DIS */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x3b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x3b); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1354,17 +1961,23 @@ static int adv7481_csi_powerup(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; uint8_t csi_sel = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | - ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | - ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txa; + if (state->csia_src == ADV7481_IP_HDMI) { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + } else { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + } + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { /* Enable 1-Lane MIPI Tx, enable pixel output and * route SD through Pixel port @@ -1373,54 +1986,57 @@ static int adv7481_csi_powerup(struct adv7481_state *state, ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(1, IO_CTRL_SD_THRU_PIX_OUT) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* Enable Tx A/B CSI #-lane */ - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_CSI_PIX_EN_SEL_ADDR, csi_sel); /* TXA MIPI lane settings for CSI */ /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* CSI TxA: Auto D-PHY Timing */ val |= ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* DPHY and CSI Tx A */ - ret |= adv7481_wr_byte(csi_map, 0xdb, 0x10); - ret |= adv7481_wr_byte(csi_map, 0xd6, 0x07); - ret |= adv7481_wr_byte(csi_map, 0xc4, 0x0a); - ret |= adv7481_wr_byte(csi_map, 0x71, 0x33); - ret |= adv7481_wr_byte(csi_map, 0x72, 0x11); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xdb, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xd6, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc4, 0x0a); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x71, 0x33); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x72, 0x11); /* CSI TxA: power up DPHY */ - ret |= adv7481_wr_byte(csi_map, 0xf0, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xf0, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x40); /* adi Recommended power up sequence */ /* DPHY and CSI Tx A Power up Sequence */ /* CSI TxA: MIPI PLL EN */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x01); msleep(200); /* CSI TxA: # MIPI Lane : Power ON */ val = ADV_REG_SETFIELD(0, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x2b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x2b); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x80); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1476,42 +2092,43 @@ static int adv7481_g_input_status(struct v4l2_subdev *sd, u32 *status) { int ret = 0; struct adv7481_state *state = to_state(sd); - uint8_t val1 = 0; - uint8_t val2 = 0; + uint8_t val = 0; uint32_t count = 0; + *status = 0; pr_debug("Enter %s\n", __func__); if (ADV7481_IP_HDMI == state->mode) { - /* - * Check Timing Lock IO Map Status3:0x71[0] && - * 0x71[1] && 0x71[7] - */ + /* Check Timing Lock */ do { - val1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - val2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), HDMI DE regeneration block NOT Locked: 0x%x, 0x%x", - __func__, __LINE__, val1, val2); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); *status |= V4L2_IN_ST_NO_SIGNAL; } } else { - val1 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(val1, SDP_RO_MAIN_IN_LOCK)) { + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + val = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(val, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) { pr_err("%s(%d), SD Input NOT Locked: 0x%x\n", - __func__, __LINE__, val1); + __func__, __LINE__, val); *status |= V4L2_IN_ST_NO_SIGNAL; } } @@ -1530,7 +2147,6 @@ static int adv7481_s_stream(struct v4l2_subdev *sd, int on) static const struct v4l2_subdev_video_ops adv7481_video_ops = { .s_routing = adv7481_s_routing, .g_frame_interval = adv7481_g_frame_interval, - .g_mbus_fmt = adv7481_g_mbus_fmt, .querystd = adv7481_query_sd_std, .g_dv_timings = adv7481_query_dv_timings, .g_input_status = adv7481_g_input_status, @@ -1539,6 +2155,11 @@ static const struct v4l2_subdev_video_ops adv7481_video_ops = { static const struct v4l2_subdev_core_ops adv7481_core_ops = { .s_power = adv7481_s_power, + .ioctl = adv7481_ioctl, +}; + +static const struct v4l2_subdev_pad_ops adv7481_pad_ops = { + .get_fmt = adv7481_get_fmt, }; static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { @@ -1548,10 +2169,13 @@ static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { static const struct v4l2_subdev_ops adv7481_ops = { .core = &adv7481_core_ops, .video = &adv7481_video_ops, + .pad = &adv7481_pad_ops, }; static int adv7481_init_v4l2_controls(struct adv7481_state *state) { + int ret = 0; + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, @@ -1565,59 +2189,195 @@ static int adv7481_init_v4l2_controls(struct adv7481_state *state) state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { - int err = state->ctrl_hdl.error; + ret = state->ctrl_hdl.error; v4l2_ctrl_handler_free(&state->ctrl_hdl); - return err; + } else { + v4l2_ctrl_handler_setup(&state->ctrl_hdl); } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); - return 0; + pr_err("%s: Exit with ret: %d\n", __func__, ret); + return ret; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static int adv7481_cci_init(struct adv7481_state *state) +{ + struct msm_camera_cci_client *cci_client = NULL; + int ret = 0; + + pr_err("%s: Enter\n", __func__); + + state->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + state->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR; + state->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + cci_client = state->i2c_client.cci_client; + if (!cci_client) { + ret = -ENOMEM; + goto err_cci_init; + } + cci_client->cci_subdev = msm_cci_get_subdev(); + pr_debug("%s cci_subdev: %p\n", __func__, cci_client->cci_subdev); + if (!cci_client->cci_subdev) { + ret = -EPROBE_DEFER; + goto err_cci_init; + } + cci_client->cci_i2c_master = state->cci_master; + cci_client->sid = state->i2c_slave_addr; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = I2C_CUSTOM_MODE; + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + pr_debug("%s i2c_client.client: %p\n", __func__, + state->i2c_client.client); + +err_cci_init: + return ret; } -static int adv7481_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7481_parse_dt(struct platform_device *pdev, + struct adv7481_state *state) +{ + struct device_node *np = state->dev->of_node; + uint32_t i = 0; + int gpio_count = 0; + struct resource *adv_addr_res = NULL; + int ret = 0; + + /* config CCI */ + ret = of_property_read_u32(np, "qcom,cci-master", + &state->cci_master); + if (ret < 0 || state->cci_master >= MASTER_MAX) { + pr_err("%s: failed to read cci master . ret %d\n", + __func__, ret); + goto exit; + } + pr_debug("%s: cci_master: 0x%x\n", __func__, state->cci_master); + adv_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!adv_addr_res) { + pr_err("%s: failed to read adv7481 resource.\n", __func__); + goto exit; + } + state->i2c_slave_addr = adv_addr_res->start; + pr_debug("%s: i2c_slave_addr: 0x%x\n", __func__, state->i2c_slave_addr); + state->i2c_io_addr = (uint8_t)state->i2c_slave_addr; + + gpio_count = of_gpio_count(np); + if (gpio_count != ADV7481_GPIO_MAX) { + ret = -EFAULT; + pr_err("%s: dt gpio count %d doesn't match required. ret %d\n", + __func__, gpio_count, ret); + goto exit; + } + for (i = 0; i < ADV7481_GPIO_MAX; i++) { + state->gpio_array[i].gpio = of_get_gpio_flags(np, i, + (enum of_gpio_flags *)&state->gpio_array[i].flags); + if (!gpio_is_valid(state->gpio_array[i].gpio)) { + pr_err("invalid gpio setting for index %d\n", i); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: gpio_array[%d] = %d flag = %ld\n", __func__, i, + state->gpio_array[i].gpio, state->gpio_array[i].flags); + } + +exit: + return ret; +} + +static const struct of_device_id adv7481_id[] = { + { .compatible = "qcom,adv7481", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, adv7481_id); + +static int adv7481_probe(struct platform_device *pdev) { struct adv7481_state *state; - struct adv7481_platform_data *pdata = NULL; + const struct of_device_id *device_id; struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; int ret; - pr_debug("Attempting to probe...\n"); - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_err("%s %s Check i2c Functionality Fail\n", - __func__, client->name); - ret = -EIO; + device_id = of_match_device(adv7481_id, &pdev->dev); + if (!device_id) { + pr_err("%s: device_id is NULL\n", __func__); + ret = -ENODEV; goto err; } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); /* Create 7481 State */ - state = devm_kzalloc(&client->dev, - sizeof(struct adv7481_state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, + sizeof(struct adv7481_state), GFP_KERNEL); if (state == NULL) { ret = -ENOMEM; - pr_err("Check Kzalloc Fail\n"); - goto err_mem; + goto err; } - state->client = client; + platform_set_drvdata(pdev, state); + state->dev = &pdev->dev; + mutex_init(&state->mutex); + ret = adv7481_parse_dt(pdev, state); + if (ret < 0) { + pr_err("Error parsing dt tree\n"); + goto err_mem_free; + } - /* Get and Check Platform Data */ - pdata = (struct adv7481_platform_data *) client->dev.platform_data; - if (!pdata) { - ret = -ENOMEM; - pr_err("Getting Platform data failed\n"); - goto err_mem; + ret = adv7481_cci_init(state); + if (ret < 0) { + pr_err("%s: failed adv7481_cci_init ret %d\n", __func__, ret); + goto err_mem_free; + } + + /* config VREG */ + ret = msm_camera_get_dt_vreg_data(pdev->dev.of_node, + &(state->cci_vreg), &(state->regulator_count)); + if (ret < 0) { + pr_err("%s:cci get_dt_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_config_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci config_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_enable_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci enable_vreg failed\n", __func__); + goto err_mem_free; } + pr_debug("%s - VREG Initialized...\n", __func__); - /* Configure and Register V4L2 I2C Sub-device */ + /* Configure and Register V4L2 Sub-device */ sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7481_ops); + v4l2_subdev_init(sd, &adv7481_ops); + sd->owner = pdev->dev.driver->owner; + v4l2_set_subdevdata(sd, state); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; @@ -1627,103 +2387,136 @@ static int adv7481_probe(struct i2c_client *client, ret = media_entity_init(&state->sd.entity, 1, &state->pad, 0); if (ret) { ret = -EIO; - pr_err("Media entity init failed\n"); + pr_err("%s(%d): Media entity init failed\n", + __func__, __LINE__); goto err_media_entity; } /* Initialize HW Config */ - ret = adv7481_hw_init(pdata, state); + ret = adv7481_hw_init(state); if (ret) { ret = -EIO; - pr_err("HW Initialisation Failed\n"); + pr_err("%s: HW Initialisation Failed\n", __func__); goto err_media_entity; } /* Register V4l2 Control Functions */ - hdl = &state->ctrl_hdl; - v4l2_ctrl_handler_init(hdl, 4); - adv7481_init_v4l2_controls(state); + ret = adv7481_init_v4l2_controls(state); + if (ret) { + pr_err("%s: V4L2 Controls Initialisation Failed %d\n", + __func__, ret); + } - /* Initials ADV7481 State Settings */ + /* Initial ADV7481 State Settings */ state->tx_auto_params = ADV7481_AUTO_PARAMS; - state->tx_lanes = ADV7481_MIPI_2LANE; /* Initialize SW Init Settings and I2C sub maps 7481 */ - ret = adv7481_dev_init(state, client); + ret = adv7481_dev_init(state); if (ret) { ret = -EIO; - pr_err("SW Initialisation Failed\n"); + pr_err("%s(%d): SW Initialisation Failed\n", + __func__, __LINE__); goto err_media_entity; } - /* Set hdmi settings */ - ret = adv7481_set_hdmi_mode(state); - /* BA registration */ - ret |= msm_ba_register_subdev_node(sd); + ret = msm_ba_register_subdev_node(sd); if (ret) { ret = -EIO; - pr_err("BA INIT FAILED\n"); + pr_err("%s: BA init failed\n", __func__); goto err_media_entity; } + enable_irq(state->irq); pr_debug("Probe successful!\n"); return ret; err_media_entity: media_entity_cleanup(&sd->entity); -err_mem: - kfree(state); + +err_mem_free: + adv7481_release_cci_clks(state); + devm_kfree(&pdev->dev, state); + err: - if (!ret) - ret = 1; return ret; } -static int adv7481_remove(struct i2c_client *client) +static int adv7481_remove(struct platform_device *pdev) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7481_state *state = to_state(sd); + struct adv7481_state *state = platform_get_drvdata(pdev); - msm_ba_unregister_subdev_node(sd); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); + msm_ba_unregister_subdev_node(&state->sd); + v4l2_device_unregister_subdev(&state->sd); + media_entity_cleanup(&state->sd.entity); v4l2_ctrl_handler_free(&state->ctrl_hdl); + adv7481_reset_irq(state); if (state->irq > 0) free_irq(state->irq, state); - i2c_unregister_device(state->i2c_csi_txa); - i2c_unregister_device(state->i2c_csi_txb); - i2c_unregister_device(state->i2c_hdmi); - i2c_unregister_device(state->i2c_edid); - i2c_unregister_device(state->i2c_cp); - i2c_unregister_device(state->i2c_sdp); - i2c_unregister_device(state->i2c_rep); + cancel_delayed_work(&state->irq_delayed_work); mutex_destroy(&state->mutex); - kfree(state); + devm_kfree(&pdev->dev, state); return 0; } -static const struct i2c_device_id adv7481_id[] = { - { DRIVER_NAME, 0 }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, adv7481_id); +#ifdef CONFIG_PM_SLEEP +static int adv7481_suspend(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* release CCI clocks */ + ret = adv7481_release_cci_clks(state); + if (ret) + pr_err("%s: adv7481 release cci clocks failed\n", __func__); + else + pr_debug("released cci clocks in suspend"); + + return 0; +} + +static int adv7481_resume(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* Request CCI clocks */ + ret = adv7481_request_cci_clks(state); + if (ret) + pr_err("%s: adv7481 request cci clocks failed\n", __func__); + else + pr_debug("requested cci clocks in resume"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(adv7481_pm_ops, adv7481_suspend, adv7481_resume); +#define ADV7481_PM_OPS (&adv7481_pm_ops) +#else +#define ADV7481_PM_OPS NULL +#endif -static struct i2c_driver adv7481_driver = { +static struct platform_driver adv7481_driver = { .driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, + .of_match_table = adv7481_id, + .pm = ADV7481_PM_OPS, }, .probe = adv7481_probe, .remove = adv7481_remove, - .id_table = adv7481_id, }; -module_i2c_driver(adv7481_driver); +module_driver(adv7481_driver, platform_driver_register, + platform_driver_unregister); MODULE_DESCRIPTION("ADI ADV7481 HDMI/MHL/SD video receiver"); diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h index a4e14fa18e0a..60b1301abbe6 100644 --- a/drivers/media/i2c/adv7481_reg.h +++ b/drivers/media/i2c/adv7481_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* 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 @@ -19,329 +19,451 @@ #define ADV_REG_GETFIELD(val, field) \ (((val) & (field##_BMSK)) >> (field##_SHFT)) +#define ADV_REG_RSTFIELD(val, field) \ + ((val) & ~((field##_BMSK) << (field##_SHFT))) + /* IO Map Registers */ -#define IO_REG_MAIN_RST_ADDR 0xFF -#define IO_REG_MAIN_RST_VALUE 0xFF - -#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 -#define IO_CTRL_RX_EN_BMSK 0x0040 -#define IO_CTRL_RX_EN_SHFT 6 -#define IO_CTRL_RX_PWDN_BMSK 0x0020 -#define IO_CTRL_RX_PWDN_SHFT 5 -#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 -#define IO_CTRL_XTAL_PWDN_SHFT 2 -#define IO_CTRL_CORE_PWDN_BMSK 0x0002 -#define IO_CTRL_CORE_PWDN_SHFT 1 -#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 -#define IO_CTRL_MASTER_PWDN_SHFT 0 - -#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 -#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F -#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 - -#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 -#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF -#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 - -#define IO_REG_CP_VID_STD_ADDR 0x05 - -#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 -#define IO_CTRL_CSI4_EN_BMSK 0x0080 -#define IO_CTRL_CSI4_EN_SHFT 7 -#define IO_CTRL_CSI1_EN_BMSK 0x0040 -#define IO_CTRL_CSI1_EN_SHFT 6 -#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 -#define IO_CTRL_PIX_OUT_EN_SHFT 5 -#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 -#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 -#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C -#define IO_CTRL_CSI4_IN_SEL_SHFT 2 - -#define IO_PAD_CTRLS_ADDR 0x0E - -#define IO_REG_I2C_CFG_ADDR 0xF2 -#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 - -#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 - -#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 -#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 -#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 -#define IO_CABLE_DET_A_RAW_BMSK 0x0040 -#define IO_CABLE_DET_A_RAW_SHFT 6 -#define IO_V_LOCKED_RAW_BMSK 0x0002 -#define IO_V_LOCKED_RAW_SHFT 1 -#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 -#define IO_DE_REGEN_LCK_RAW_SHFT 0 +#define IO_REG_MAIN_RST_ADDR 0xFF +#define IO_REG_MAIN_RST_VALUE 0xFF + +#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 +#define IO_CTRL_RX_EN_BMSK 0x0040 +#define IO_CTRL_RX_EN_SHFT 6 +#define IO_CTRL_RX_PWDN_BMSK 0x0020 +#define IO_CTRL_RX_PWDN_SHFT 5 +#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 +#define IO_CTRL_XTAL_PWDN_SHFT 2 +#define IO_CTRL_CORE_PWDN_BMSK 0x0002 +#define IO_CTRL_CORE_PWDN_SHFT 1 +#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 +#define IO_CTRL_MASTER_PWDN_SHFT 0 + +#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 +#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F +#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 + +#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 +#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF +#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 + +#define IO_REG_CP_VID_STD_ADDR 0x05 + +#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 +#define IO_CTRL_CSI4_EN_BMSK 0x0080 +#define IO_CTRL_CSI4_EN_SHFT 7 +#define IO_CTRL_CSI1_EN_BMSK 0x0040 +#define IO_CTRL_CSI1_EN_SHFT 6 +#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 +#define IO_CTRL_PIX_OUT_EN_SHFT 5 +#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 +#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 +#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C +#define IO_CTRL_CSI4_IN_SEL_SHFT 2 + +#define IO_PAD_CTRLS_ADDR 0x0E +#define IO_PAD_FILTER_CTRLS_ADDR 0x0F + +#define IO_REG_I2C_CFG_ADDR 0xF2 +#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 + +#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 /* Interrupts */ -#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 -#define IO_CABLE_DET_A_ST_BMSK 0x0040 -#define IO_CABLE_DET_A_ST_SHFT 6 - -#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 -#define IO_CABLE_DET_A_CLR_BMSK 0x0040 -#define IO_CABLE_DET_A_CLR_SHFT 6 - -#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 -#define IO_CABLE_DET_A_MB2_BMSK 0x0040 -#define IO_CABLE_DET_A_MB2_SHFT 6 - -#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 -#define IO_CABLE_DET_A_MB1_BMSK 0x0040 -#define IO_CABLE_DET_A_MB1_SHFT 6 - -#define IO_REG_PAD_CTRL_1_ADDR 0x1D -#define IO_PDN_INT1_BMSK 0x0080 -#define IO_PDN_INT1_SHFT 7 -#define IO_PDN_INT2_BMSK 0x0040 -#define IO_PDN_INT2_SHFT 6 -#define IO_PDN_INT3_BMSK 0x0020 -#define IO_PDN_INT3_SHFT 5 -#define IO_INV_LLC_BMSK 0x0010 -#define IO_INV_LLC_SHFT 4 -#define IO_DRV_LLC_PAD_BMSK 0x000C -#define IO_DRV_LLC_PAD_SHFT 2 - -#define IO_REG_INT_RAW_STATUS_ADDR 0x3F - -#define IO_REG_INT1_CONF_ADDR 0x40 -#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ_DUR_SEL_SHFT 6 -#define IO_INTRQ_OP_SEL_BMSK 0x0003 -#define IO_INTRQ_OP_SEL_SHFT 0 - -#define IO_REG_INT2_CONF_ADDR 0x41 -#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ2_DUR_SEL_SHFT 6 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 -#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 -#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 -#define IO_INT2_EN_BMSK 0x0004 -#define IO_INT2_EN_SHFT 2 -#define IO_INTRQ2_OP_SEL_BMSK 0x0003 -#define IO_INTRQ2_OP_SEL_SHFT 0 - -#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 -#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 -#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 - -#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 -#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 -#define IO_CP_LOCK_CP_MB1_SHFT 7 -#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 -#define IO_CP_UNLOCK_CP_MB1_SHFT 6 -#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 -#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 -#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 -#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 -#define IO_INT_SD_MB1_BMSK 0x0001 -#define IO_INT_SD_MB1_SHFT 0 +#define IO_HDMI_LVL_INT_CLEAR_1_ADDR 0x69 + +#define IO_HDMI_LVL_INT_MASKB_1_ADDR 0x6B +#define IO_AVI_INFO_MB1_BMSK 0x0001 +#define IO_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_2_ADDR 0x6E + +#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 +#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 +#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 +#define IO_CABLE_DET_A_RAW_BMSK 0x0040 +#define IO_CABLE_DET_A_RAW_SHFT 6 +#define IO_V_LOCKED_RAW_BMSK 0x0002 +#define IO_V_LOCKED_RAW_SHFT 1 +#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 +#define IO_DE_REGEN_LCK_RAW_SHFT 0 + +#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 +#define IO_CABLE_DET_A_ST_BMSK 0x0040 +#define IO_CABLE_DET_A_ST_SHFT 6 +#define IO_V_LOCKED_ST_BMSK 0x0002 +#define IO_V_LOCKED_ST_SHFT 1 +#define IO_DE_REGEN_LCK_ST_BMSK 0x0001 +#define IO_DE_REGEN_LCK_ST_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 +#define IO_CABLE_DET_A_CLR_BMSK 0x0040 +#define IO_CABLE_DET_A_CLR_SHFT 6 + +#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 +#define IO_CABLE_DET_A_MB2_BMSK 0x0040 +#define IO_CABLE_DET_A_MB2_SHFT 6 + +#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 +#define IO_CABLE_DET_A_MB1_BMSK 0x0040 +#define IO_CABLE_DET_A_MB1_SHFT 6 +#define IO_V_LOCKED_MB1_BMSK 0x0002 +#define IO_V_LOCKED_MB1_SHFT 1 +#define IO_DE_REGEN_LCK_MB1_BMSK 0x0001 +#define IO_DE_REGEN_LCK_MB1_SHFT 0 + +#define IO_HDMI_EDG_RAW_STATUS_1_ADDR 0x80 +#define IO_NEW_AVI_INFO_RAW_BMSK 0x0001 +#define IO_NEW_AVI_INFO_RAW_SHFT 0 + +#define IO_HDMI_EDG_INT_STATUS_1_ADDR 0x81 +#define IO_NEW_AVI_INFO_ST_BMSK 0x0001 +#define IO_NEW_AVI_INFO_ST_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_1_ADDR 0x82 +#define IO_NEW_AVI_INFO_CLR_BMSK 0x0001 +#define IO_NEW_AVI_INFO_CLR_SHFT 0 + +#define IO_HDMI_EDG_INT2_MASKB_1_ADDR 0x83 +#define IO_NEW_AVI_INFO_MB2_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB2_SHFT 0 + +#define IO_HDMI_EDG_INT_MASKB_1_ADDR 0x84 +#define IO_NEW_AVI_INFO_MB1_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_2_ADDR 0x87 +#define IO_HDMI_EDG_INT_CLEAR_3_ADDR 0x8C + +#define IO_REG_PAD_CTRL_1_ADDR 0x1D +#define IO_PDN_INT1_BMSK 0x0080 +#define IO_PDN_INT1_SHFT 7 +#define IO_PDN_INT2_BMSK 0x0040 +#define IO_PDN_INT2_SHFT 6 +#define IO_PDN_INT3_BMSK 0x0020 +#define IO_PDN_INT3_SHFT 5 +#define IO_INV_LLC_BMSK 0x0010 +#define IO_INV_LLC_SHFT 4 +#define IO_DRV_LLC_PAD_BMSK 0x000C +#define IO_DRV_LLC_PAD_SHFT 2 + +#define IO_REG_INT_RAW_STATUS_ADDR 0x3F +#define IO_INT_CEC_ST_BMSK 0x0010 +#define IO_INT_CEC_ST_SHFT 4 +#define IO_INT_HDMI_ST_BMSK 0x0008 +#define IO_INT_HDMI_ST_SHFT 3 +#define IO_INTRQ3_RAW_BMSK 0x0004 +#define IO_INTRQ3_RAW_SHFT 2 +#define IO_INTRQ2_RAW_BMSK 0x0002 +#define IO_INTRQ2_RAW_SHFT 1 +#define IO_INTRQ1_RAW_BMSK 0x0001 +#define IO_INTRQ1_RAW_SHFT 0 + +#define IO_REG_INT1_CONF_ADDR 0x40 +#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ_DUR_SEL_SHFT 6 +#define IO_INTRQ_OP_SEL_BMSK 0x0003 +#define IO_INTRQ_OP_SEL_SHFT 0 + +#define IO_REG_INT2_CONF_ADDR 0x41 +#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ2_DUR_SEL_SHFT 6 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 +#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 +#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 +#define IO_INT2_EN_BMSK 0x0004 +#define IO_INT2_EN_SHFT 2 +#define IO_INTRQ2_OP_SEL_BMSK 0x0003 +#define IO_INTRQ2_OP_SEL_SHFT 0 + +#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 +#define IO_CP_LOCK_CP_RAW_BMSK 0x0080 +#define IO_CP_LOCK_CP_RAW_SHFT 7 +#define IO_CP_UNLOCK_CP_RAW_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_RAW_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_RAW_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_RAW_SHFT 5 +#define IO_MPU_STIM_INTRQ_RAW_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_RAW_SHFT 1 +#define IO_INT_SD_RAW_BMSK 0x0001 +#define IO_INT_SD_RAW_SHFT 0 + +#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 +#define IO_CP_LOCK_CP_ST_BMSK 0x0080 +#define IO_CP_LOCK_CP_ST_SHFT 7 +#define IO_CP_UNLOCK_CP_ST_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_ST_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_ST_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_ST_SHFT 5 +#define IO_MPU_STIM_INTRQ_ST_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_ST_SHFT 1 +#define IO_INT_SD_ST_BMSK 0x0001 +#define IO_INT_SD_ST_SHFT 0 + +#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 + +#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 +#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 +#define IO_CP_LOCK_CP_MB1_SHFT 7 +#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_MB1_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 +#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 +#define IO_INT_SD_MB1_BMSK 0x0001 +#define IO_INT_SD_MB1_SHFT 0 + +#define IO_REG_CHIP_REV_ID_1_ADDR 0xDF +#define IO_REG_CHIP_REV_ID_2_ADDR 0xE0 /* Offsets */ -#define IO_REG_DPLL_ADDR 0xF3 -#define IO_REG_CP_ADDR 0xF4 -#define IO_REG_HDMI_ADDR 0xF5 -#define IO_REG_EDID_ADDR 0xF6 -#define IO_REG_HDMI_REP_ADDR 0xF7 -#define IO_REG_HDMI_INF_ADDR 0xF8 -#define IO_REG_CBUS_ADDR 0xF9 -#define IO_REG_CEC_ADDR 0xFA -#define IO_REG_SDP_ADDR 0xFB -#define IO_REG_CSI_TXB_ADDR 0xFC -#define IO_REG_CSI_TXA_ADDR 0xFD +#define IO_REG_DPLL_ADDR 0xF3 +#define IO_REG_CP_ADDR 0xF4 +#define IO_REG_HDMI_ADDR 0xF5 +#define IO_REG_EDID_ADDR 0xF6 +#define IO_REG_HDMI_REP_ADDR 0xF7 +#define IO_REG_HDMI_INF_ADDR 0xF8 +#define IO_REG_CBUS_ADDR 0xF9 +#define IO_REG_CEC_ADDR 0xFA +#define IO_REG_SDP_ADDR 0xFB +#define IO_REG_CSI_TXB_ADDR 0xFC +#define IO_REG_CSI_TXA_ADDR 0xFD /* Sub Address Map Locations */ -#define IO_REG_DPLL_SADDR 0x4C -#define IO_REG_CP_SADDR 0x44 -#define IO_REG_HDMI_SADDR 0x74 -#define IO_REG_EDID_SADDR 0x78 -#define IO_REG_HDMI_REP_SADDR 0x64 -#define IO_REG_HDMI_INF_SADDR 0x62 -#define IO_REG_CBUS_SADDR 0xF0 -#define IO_REG_CEC_SADDR 0x82 -#define IO_REG_SDP_SADDR 0xF2 -#define IO_REG_CSI_TXB_SADDR 0x90 -#define IO_REG_CSI_TXA_SADDR 0x94 +#define IO_REG_DPLL_SADDR 0x4C +#define IO_REG_CP_SADDR 0x44 +#define IO_REG_HDMI_SADDR 0x74 +#define IO_REG_EDID_SADDR 0x78 +#define IO_REG_HDMI_REP_SADDR 0x64 +#define IO_REG_HDMI_INF_SADDR 0x62 +#define IO_REG_CBUS_SADDR 0xF0 +#define IO_REG_CEC_SADDR 0x82 +#define IO_REG_SDP_SADDR 0xF2 +#define IO_REG_CSI_TXB_SADDR 0x90 +#define IO_REG_CSI_TXA_SADDR 0x94 /* HDMI Map Registers */ -#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 -#define HDMI_REG_AV_MUTE_BMSK 0x0040 -#define HDMI_REG_AV_MUTE_SHFT 6 -#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 -#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 -#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 -#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 - -#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 -#define HDMI_REG_HDMI_MODE_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 -#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 -#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 -#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 -#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 -#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 -#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F -#define HDMI_REG_PIXEL_REPETITION_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 -#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 -#define HDMI_VERT_FILTER_LOCKED_SHFT 7 -#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 -#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 -#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 -#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 -#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F -#define HDMI_REG_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 -#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 -#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F -#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 -#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A -#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 - -#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B -#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 -#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 -#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 -#define HDMI_REG_HDMI_INTERLACED_SHFT 5 - -#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E -#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F -#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F -#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 - -#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 -#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 -#define HDMI_DIS_CABLE_DET_RST_SHFT 6 - -#define HDMI_REG_TMDS_FREQ_ADDR 0x51 -#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF -#define HDMI_REG_TMDS_FREQ_SHFT 0 - -#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 -#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F -#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 - -#define HDMI_REG_RST_CTRLS_ADDR 0x5A -#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 -#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 - -#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E -#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 -#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 +#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 +#define HDMI_REG_AV_MUTE_BMSK 0x0040 +#define HDMI_REG_AV_MUTE_SHFT 6 +#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 +#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 +#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 +#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 + +#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 +#define HDMI_REG_HDMI_MODE_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 +#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 +#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 +#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 +#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 +#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 +#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F +#define HDMI_REG_PIXEL_REPETITION_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 +#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 +#define HDMI_VERT_FILTER_LOCKED_SHFT 7 +#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 +#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 +#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 +#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 +#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F +#define HDMI_REG_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 +#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 +#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F +#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 +#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A +#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 + +#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B +#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 +#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 +#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 +#define HDMI_REG_HDMI_INTERLACED_SHFT 5 + +#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E +#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F +#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F +#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 + +#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 +#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 +#define HDMI_DIS_CABLE_DET_RST_SHFT 6 + +#define HDMI_REG_TMDS_FREQ_ADDR 0x51 +#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF +#define HDMI_REG_TMDS_FREQ_SHFT 0 + +#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 +#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F +#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 + +#define HDMI_REG_RST_CTRLS_ADDR 0x5A +#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 +#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 + +#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E +#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 +#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 /* HDMI Repeater Map Registers */ -#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 -#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 +#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 +#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 + +#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 +#define HDMI_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_EDID_A_ENABLE_SHFT 0 + +/* CEC Map Registers */ +#define CEC_REG_LOG_ADDR_MASK_ADDR 0x27 +#define CEC_REG_LOG_ADDR_MASK2_BMSK 0x0040 +#define CEC_REG_LOG_ADDR_MASK2_SHFT 6 +#define CEC_REG_LOG_ADDR_MASK1_BMSK 0x0020 +#define CEC_REG_LOG_ADDR_MASK1_SHFT 5 +#define CEC_REG_LOG_ADDR_MASK0_BMSK 0x0010 +#define CEC_REG_LOG_ADDR_MASK0_SHFT 4 +#define CEC_REG_ERROR_REPORT_MODE_BMSK 0x0008 +#define CEC_REG_ERROR_REPORT_MODE_SHFT 3 +#define CEC_REG_ERROR_REPORT_DET_BMSK 0x0004 +#define CEC_REG_ERROR_REPORT_DET_SHFT 2 +#define CEC_REG_FORCE_NACK_BMSK 0x0002 +#define CEC_REG_FORCE_NACK_SHFT 1 +#define CEC_REG_FORCE_IGNORE_BMSK 0x0001 +#define CEC_REG_FORCE_IGNORE_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS0_1_ADDR 0x28 +#define CEC_REG_LOGICAL_ADDRESS1_BMSK 0x00F0 +#define CEC_REG_LOGICAL_ADDRESS1_SHFT 4 +#define CEC_REG_LOGICAL_ADDRESS0_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS0_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS2_ADDR 0x29 +#define CEC_REG_LOGICAL_ADDRESS2_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS2_SHFT 0 + +#define CEC_REG_CEC_POWER_UP_ADDR 0x2A +#define CEC_REG_CEC_POWER_UP_BMSK 0x0001 +#define CEC_REG_CEC_POWER_UP_SHFT 0 + +#define CEC_REG_CLR_RX_RDY_SFT_RST_ADDR 0x2C +#define CEC_REG_CEC_SOFT_RESET_BMSK 0x0001 +#define CEC_REG_CEC_SOFT_RESET_SHFT 0 -#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 -#define HDMI_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_EDID_A_ENABLE_SHFT 0 /* CP Map Registers */ -#define CP_REG_CONTRAST 0x3A -#define CP_REG_SATURATION 0x3B -#define CP_REG_BRIGHTNESS 0x3C -#define CP_REG_HUE 0x3D -#define CP_REG_VID_ADJ 0x3E -#define CP_CTR_VID_ADJ_EN 0x80 -#define CP_REG_STDI_CH_ADDR 0xB1 -#define CP_STDI_DVALID_CH1_BMSK 0x0080 -#define CP_STDI_DVALID_CH1_SHFT 7 +#define CP_REG_CONTRAST 0x3A +#define CP_REG_SATURATION 0x3B +#define CP_REG_BRIGHTNESS 0x3C +#define CP_REG_HUE 0x3D +#define CP_REG_VID_ADJ 0x3E +#define CP_CTR_VID_ADJ_EN 0x80 +#define CP_REG_STDI_CH_ADDR 0xB1 +#define CP_STDI_DVALID_CH1_BMSK 0x0080 +#define CP_STDI_DVALID_CH1_SHFT 7 + +/* SDP Main Map */ +#define SDP_RW_MAP_REG 0x0e + +/* SDP MAP 1 Registers */ +#define SDP_RW_LOCK_UNLOCK_CLR_ADDR 0x43 +#define SDP_RW_LOCK_UNLOCK_MASK_ADDR 0x44 /* SDP R/O Main Map Registers */ -#define SDP_RO_MAIN_STATUS1_ADDR 0x10 -#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 -#define SDP_RO_MAIN_COL_KILL_SHFT 7 -#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 -#define SDP_RO_MAIN_AD_RESULT_SHFT 4 -#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 -#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 -#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 -#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 -#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 -#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 -#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 -#define SDP_RO_MAIN_IN_LOCK_SHFT 0 +#define SDP_RO_MAIN_STATUS1_ADDR 0x10 +#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 +#define SDP_RO_MAIN_COL_KILL_SHFT 7 +#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 +#define SDP_RO_MAIN_AD_RESULT_SHFT 4 +#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 +#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 +#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 +#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 +#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 +#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 +#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 +#define SDP_RO_MAIN_IN_LOCK_SHFT 0 + /* * CSI Map Registers */ -#define CSI_REG_TX_CFG1_ADDR 0x00 -#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 -#define CSI_CTRL_TX_PWRDN_SHFT 7 -#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 -#define CSI_CTRL_AUTO_PARAMS_SHFT 5 -#define CSI_CTRL_NUM_LANES_BMSK 0x0007 -#define CSI_CTRL_NUM_LANES_SHFT 0 - -#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 -#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 -#define CSI_CTRL_DPHY_PWDN_SHFT 0 +#define CSI_REG_TX_CFG1_ADDR 0x00 +#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 +#define CSI_CTRL_TX_PWRDN_SHFT 7 +#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 +#define CSI_CTRL_AUTO_PARAMS_SHFT 5 +#define CSI_CTRL_NUM_LANES_BMSK 0x0007 +#define CSI_CTRL_NUM_LANES_SHFT 0 + +#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 +#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 +#define CSI_CTRL_DPHY_PWDN_SHFT 0 enum adv7481_adresult { - AD_NTSM_M_J = 0x0, - AD_NTSC_4_43 = 0x1, - AD_PAL_M = 0x2, - AD_PAL_60 = 0x3, - AD_PAL_B_G = 0x4, - AD_SECAM = 0x5, - AD_PAL_COMB_N = 0x6, - AD_SECAM_525 = 0x7, + AD_NTSM_M_J = 0x0, + AD_NTSC_4_43 = 0x1, + AD_PAL_M = 0x2, + AD_PAL_60 = 0x3, + AD_PAL_B_G = 0x4, + AD_SECAM = 0x5, + AD_PAL_COMB_N = 0x6, + AD_SECAM_525 = 0x7, }; enum adv7481_color_depth { - CD_8BIT = 0x0, - CD_10BIT = 0x1, - CD_12BIT = 0x2, - CD_16BIT = 0x3, + CD_8BIT = 0x0, + CD_10BIT = 0x1, + CD_12BIT = 0x2, + CD_16BIT = 0x3, }; enum adv7481_intrq_dur_sel { - AD_4_XTAL_PER = 0x0, - AD_16_XTAL_PER = 0x1, - AD_64_XTAL_PER = 0x2, - AD_ACTIVE_UNTIL_CLR = 0x3, + AD_4_XTAL_PER = 0x0, + AD_16_XTAL_PER = 0x1, + AD_64_XTAL_PER = 0x2, + AD_ACTIVE_UNTIL_CLR = 0x3, }; enum adv7481_intrq_op_sel { - AD_OP_OPEN_DRAIN = 0x0, - AD_OP_DRIVE_LOW = 0x1, - AD_OP_DRIVE_HIGH = 0x2, - AD_OP_DISABLED = 0x3, + AD_OP_OPEN_DRAIN = 0x0, + AD_OP_DRIVE_LOW = 0x1, + AD_OP_DRIVE_HIGH = 0x2, + AD_OP_DISABLED = 0x3, }; enum adv7481_drv_llc_pad { - AD_LLC_PAD_NOT_USED = 0x0, - AD_MIN_DRIVE_STRNGTH = 0x1, - AD_MID_DRIVE_STRNGTH = 0x2, - AD_MAX_DRIVE_STRNGTH = 0x3, + AD_LLC_PAD_NOT_USED = 0x0, + AD_MIN_DRIVE_STRNGTH = 0x1, + AD_MID_DRIVE_STRNGTH = 0x2, + AD_MAX_DRIVE_STRNGTH = 0x3, }; #endif diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c index fcd803d4b138..2133f9391433 100644 --- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c @@ -212,8 +212,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, mapped_info->paddr += accu_length; accu_length += qbuf_buf->planes[i].length; - CDBG("%s: plane: %d addr:%lu\n", - __func__, i, (unsigned long)mapped_info->paddr); + CDBG("%s: plane: %d addr:%pK\n", + __func__, i, (void *)mapped_info->paddr); } buf_info->num_planes = qbuf_buf->num_planes; @@ -275,8 +275,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr, pr_err_ratelimited("%s: cannot map address", __func__); goto smmu_map_error; } - CDBG("%s: addr:%lu\n", - __func__, (unsigned long)mapped_info->paddr); + CDBG("%s: addr:%pK\n", + __func__, (void *)mapped_info->paddr); return rc; smmu_map_error: @@ -1367,14 +1367,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, struct msm_isp_buffer *bufs = NULL; uint32_t i = 0, j = 0, k = 0, rc = 0; char *print_buf = NULL, temp_buf[100]; - uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000; + uint32_t print_buf_size = 2000; + unsigned long start_addr = 0, end_addr = 0; int buf_addr_delta = -1; int temp_delta = 0; uint32_t debug_output_id = 0; uint32_t debug_buf_idx = 0; uint32_t debug_buf_plane = 0; - uint32_t debug_start_addr = 0; - uint32_t debug_end_addr = 0; + unsigned long debug_start_addr = 0; + unsigned long debug_end_addr = 0; uint32_t debug_frame_id = 0; enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED; unsigned long flags; @@ -1433,8 +1434,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, debug_output_id, debug_frame_id); pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__, debug_buf_idx, debug_buf_plane, debug_state); - pr_err("%s: buf address 0x%x -- 0x%x\n", __func__, - debug_start_addr, debug_end_addr); + pr_err("%s: buf address %pK -- %pK\n", __func__, + (void *)debug_start_addr, (void *)debug_end_addr); if (BUF_DEBUG_FULL) { print_buf = kzalloc(print_buf_size, GFP_ATOMIC); @@ -1469,9 +1470,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, mapped_info[k].len; snprintf(temp_buf, sizeof(temp_buf), - " buf %d plane %d start_addr %x end_addr %x\n", - j, k, start_addr, - end_addr); + " buf %d plane %d start_addr %pK end_addr %pK\n", + j, k, + (void *)start_addr, + (void *)end_addr); strlcat(print_buf, temp_buf, print_buf_size); } diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c index 2030755d59d0..7d9edde62c1b 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c @@ -1360,11 +1360,12 @@ int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data) /* set attributes */ ret = iommu_domain_set_attr(domain, cb->attr, (void *)data); if (ret < 0) { + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); pr_err("Error: set attr\n"); return -ENODEV; } } else { - return -EINVAL; + ret = -EINVAL; } mutex_unlock(&iommu_cb_set.cb_info[idx].lock); return ret; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 5dbed80f5b85..8f3cffb4c3da 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -1166,10 +1166,15 @@ int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr, { struct msm_vfe_smmu_attach_cmd *cmd = arg; int rc = 0; + int32_t stall_disable = 1; pr_debug("%s: cmd->security_mode : %d\n", __func__, cmd->security_mode); + mutex_lock(&buf_mgr->lock); if (cmd->iommu_attach_mode == IOMMU_ATTACH) { + /* disable smmu stall on fault */ + cam_smmu_set_attr(buf_mgr->iommu_hdl, + DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable); /* * Call hypervisor thru scm call to notify secure or * non-secure mode diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 08a8135ea9f4..7e74f2f10c8c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1259,7 +1259,7 @@ void msm_isp_get_avtimer_ts( int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) { - int rc = 0, i; + int rc = 0, i = 0; uint32_t io_format = 0; struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg; struct msm_vfe_axi_stream *stream_info; @@ -1395,7 +1395,7 @@ done: int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) { - int rc = 0, i; + int rc = 0, i = 0; struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_stream_cfg_cmd stream_cfg; diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 24e3223a79d0..ab7d4e86dcac 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -621,7 +621,7 @@ static int msm_ispif_reset(struct ispif_device *ispif) ispif->base + ISPIF_VFE_m_INTF_CMD_0(i)); msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY, ispif->base + ISPIF_VFE_m_INTF_CMD_1(i)); - pr_debug("%s: base %lx", __func__, (unsigned long)ispif->base); + pr_debug("%s: base %pK", __func__, ispif->base); msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0)); msm_camera_io_w(0, ispif->base + diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig index c0e73813ab06..d2c2e90f9de9 100644 --- a/drivers/media/platform/msm/sde/Kconfig +++ b/drivers/media/platform/msm/sde/Kconfig @@ -21,6 +21,7 @@ config MSM_SDE_HDMI_CEC depends on DRM_SDE_HDMI select MEDIA_CEC_SUPPORT select MEDIA_CEC_EDID + select MEDIA_CEC_NOTIFIER ---help--- The HDMI CEC driver provides support to enable HDMI CEC features which allows various audiovisual products to communicate using HDMI diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c index 0fed19a01d5b..b3798e8a9d24 100644 --- a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c +++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c @@ -18,11 +18,14 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/cec.h> #include <media/cec.h> +#include <media/cec-edid.h> +#include <media/cec-notifier.h> #include "sde_hdmi_cec_util.h" @@ -63,6 +66,7 @@ struct sde_hdmi_cec { struct cec_hw_resource hw_res; int irq; enum cec_irq_status irq_status; + struct cec_notifier *notifier; }; static int sde_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) @@ -287,6 +291,8 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sde_hdmi_cec *cec; + struct device_node *np; + struct platform_device *hdmi_dev; int ret; cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); @@ -295,6 +301,15 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) cec->dev = dev; + np = of_parse_phandle(pdev->dev.of_node, "qcom,hdmi-dev", 0); + if (!np) { + pr_err("failed to find hdmi node in device tree\n"); + return -ENODEV; + } + hdmi_dev = of_find_device_by_node(np); + if (hdmi_dev == NULL) + return -EPROBE_DEFER; + cec->irq = of_irq_get(dev->of_node, 0); if (cec->irq < 0) { pr_err("failed to get irq\n"); @@ -314,24 +329,38 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) cec->adap = cec_allocate_adapter(&sde_hdmi_cec_adap_ops, cec, CEC_NAME, CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | - CEC_CAP_PHYS_ADDR | CEC_CAP_TRANSMIT, 1); + CEC_CAP_TRANSMIT, 1); ret = PTR_ERR_OR_ZERO(cec->adap); if (ret) return ret; ret = cec_register_adapter(cec->adap, &pdev->dev); - if (ret) { - cec_delete_adapter(cec->adap); - return ret; + if (ret) + goto err_del_adap; + + cec->notifier = cec_notifier_get(&hdmi_dev->dev); + if (!cec->notifier) { + pr_err("failed to get cec notifier\n"); + goto err_del_adap; } platform_set_drvdata(pdev, cec); pm_runtime_enable(dev); + /* + * cec_register_cec_notifier has to be later than pm_runtime_enable + * because it calls adap_enable. + */ + cec_register_cec_notifier(cec->adap, cec->notifier); + pr_debug("probe done\n"); - return 0; + return ret; + +err_del_adap: + cec_delete_adapter(cec->adap); + return ret; } static int sde_hdmi_cec_remove(struct platform_device *pdev) @@ -341,6 +370,7 @@ static int sde_hdmi_cec_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); cec_unregister_adapter(cec->adap); + cec_notifier_put(cec->notifier); devm_free_irq(&pdev->dev, cec->irq, cec); sde_hdmi_cec_deinit_resource(pdev, &cec->hw_res); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c index 5b574ed9fabc..9e3d7d806a0c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -145,7 +145,7 @@ static struct sde_mdp_hw_resource *sde_rotator_hw_alloc( struct sde_mdp_hw_resource *mdp_hw; struct sde_rot_data_type *mdata = sde_rot_get_mdata(); int pipe_ndx, offset = ctl_id; - int ret; + int ret = 0; mdp_hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct sde_mdp_hw_resource), GFP_KERNEL); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 69e51cc96303..29c57d13744e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1017,7 +1017,7 @@ static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev, { struct mmc_blk_ioc_rpmb_data *idata; struct mmc_blk_data *md; - struct mmc_card *card; + struct mmc_card *card = NULL; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct mmc_request mrq = {NULL}; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 63f7bf87843f..2cb0ea03a338 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -616,17 +616,39 @@ static int mmc_devfreq_create_freq_table(struct mmc_host *host) host->card->clk_scaling_lowest, host->card->clk_scaling_highest); + /* + * Create the frequency table and initialize it with default values. + * Initialize it with platform specific frequencies if the frequency + * table supplied by platform driver is present, otherwise initialize + * it with min and max frequencies supported by the card. + */ if (!clk_scaling->freq_table) { - pr_debug("%s: no frequency table defined - setting default\n", - mmc_hostname(host)); + if (clk_scaling->pltfm_freq_table_sz) + clk_scaling->freq_table_sz = + clk_scaling->pltfm_freq_table_sz; + else + clk_scaling->freq_table_sz = 2; + clk_scaling->freq_table = kzalloc( - 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL); + (clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->freq_table))), GFP_KERNEL); if (!clk_scaling->freq_table) return -ENOMEM; - clk_scaling->freq_table[0] = host->card->clk_scaling_lowest; - clk_scaling->freq_table[1] = host->card->clk_scaling_highest; - clk_scaling->freq_table_sz = 2; - goto out; + + if (clk_scaling->pltfm_freq_table) { + memcpy(clk_scaling->freq_table, + clk_scaling->pltfm_freq_table, + (clk_scaling->pltfm_freq_table_sz * + sizeof(*(clk_scaling->pltfm_freq_table)))); + } else { + pr_debug("%s: no frequency table defined - setting default\n", + mmc_hostname(host)); + clk_scaling->freq_table[0] = + host->card->clk_scaling_lowest; + clk_scaling->freq_table[1] = + host->card->clk_scaling_highest; + goto out; + } } if (host->card->clk_scaling_lowest > @@ -835,7 +857,7 @@ int mmc_resume_clk_scaling(struct mmc_host *host) devfreq_min_clk = host->clk_scaling.freq_table[0]; host->clk_scaling.curr_freq = devfreq_max_clk; - if (host->ios.clock < host->card->clk_scaling_highest) + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) host->clk_scaling.curr_freq = devfreq_min_clk; host->clk_scaling.clk_scaling_in_progress = false; @@ -895,6 +917,10 @@ int mmc_exit_clk_scaling(struct mmc_host *host) host->clk_scaling.devfreq = NULL; atomic_set(&host->clk_scaling.devfreq_abort, 1); + + kfree(host->clk_scaling.freq_table); + host->clk_scaling.freq_table = NULL; + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); return 0; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index ca72ebfd55a3..1eeab7db9722 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1837,13 +1837,13 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, } if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table", - &msm_host->mmc->clk_scaling.freq_table, - &msm_host->mmc->clk_scaling.freq_table_sz, 0)) + &msm_host->mmc->clk_scaling.pltfm_freq_table, + &msm_host->mmc->clk_scaling.pltfm_freq_table_sz, 0)) pr_debug("%s: no clock scaling frequencies were supplied\n", dev_name(dev)); - else if (!msm_host->mmc->clk_scaling.freq_table || - !msm_host->mmc->clk_scaling.freq_table_sz) - dev_err(dev, "bad dts clock scaling frequencies\n"); + else if (!msm_host->mmc->clk_scaling.pltfm_freq_table || + !msm_host->mmc->clk_scaling.pltfm_freq_table_sz) + dev_err(dev, "bad dts clock scaling frequencies\n"); /* * Few hosts can support DDR52 mode at the same lower diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 06ea6cc9e30a..096818610d40 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -38,28 +38,28 @@ static int __ath_regd_init(struct ath_regulatory *reg); /* We enable active scan on these a case by case basis by regulatory domain */ #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ +#define ATH9K_2GHZ_CH14 REG_RULE(2484 - 10, 2484 + 10, 20, 0, 20,\ NL80211_RRF_NO_IR | \ NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ -#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5180_5320 REG_RULE(5180 - 10, 5320 + 10, 160, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5500_5825 REG_RULE(5500 - 10, 5825 + 10, 80, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5745_5825 REG_RULE(5745 - 10, 5825 + 10, 80, 0, 20,\ NL80211_RRF_NO_IR) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ ATH9K_2GHZ_CH12_13, \ ATH9K_2GHZ_CH14 -#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \ - ATH9K_5GHZ_5470_5850 +#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5180_5320, \ + ATH9K_5GHZ_5500_5825 /* This one skips what we call "mid band" */ -#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ - ATH9K_5GHZ_5725_5850 +#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5180_5320, \ + ATH9K_5GHZ_5745_5825 /* Can be used for: * 0x60, 0x61, 0x62 */ @@ -256,7 +256,7 @@ EXPORT_SYMBOL(ath_is_49ghz_allowed); /* Frequency is one where radar detection is required */ static bool ath_is_radar_freq(u16 center_freq) { - return (center_freq >= 5260 && center_freq <= 5700); + return (center_freq >= 5260 && center_freq <= 5720); } static void ath_force_clear_no_ir_chan(struct wiphy *wiphy, diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index b4faf8212348..ce1f384e7f8e 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -98,6 +98,10 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) } } + /* Wake all queues */ + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); + out: if (rc) set_bit(wil_status_suspended, wil->status); @@ -113,6 +117,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) /* Prevent handling of new tx and wmi commands */ set_bit(wil_status_suspending, wil->status); + wil_update_net_queues_bh(wil, NULL, true); if (!wil_is_tx_idle(wil)) { wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); @@ -201,13 +206,17 @@ resume_after_fail: clear_bit(wil_status_suspending, wil->status); rc = wmi_resume(wil); /* if resume succeeded, reject the suspend */ - if (!rc) + if (!rc) { rc = -EBUSY; - + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); + } return rc; reject_suspend: clear_bit(wil_status_suspending, wil->status); + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); return -EBUSY; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 439fe30936b6..8f1e79b425cf 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1930,6 +1930,11 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, return; } + /* Do not wake the queues in suspend flow */ + if (test_bit(wil_status_suspending, wil->status) || + test_bit(wil_status_suspended, wil->status)) + return; + /* check wake */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { struct vring *cur_vring = &wil->vring_tx[i]; diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c index e1c50e1273ac..4354b2600472 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.c +++ b/drivers/platform/msm/mhi/mhi_bhi.c @@ -249,6 +249,13 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic) { struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table; + struct bhie_mem_info *bhie_mem_info; + u32 rx_sequence, val, current_seq; + u32 timeout = (bhi_ctxt->poll_timeout * 1000) / BHIE_RDDM_DELAY_TIME_US; + int i; + u32 cur_exec, prev_exec = 0; + u32 state, prev_state = 0; + u32 rx_status, prev_status = 0; if (!rddm_table->bhie_mem_info) { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "RDDM table == NULL\n"); @@ -258,9 +265,93 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic) if (!in_panic) return bhi_rddm_graceful(mhi_dev_ctxt); - mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "RDDM collection in panic not yet supported\n"); - return -EINVAL; + /* + * Below code should only be executed during kernel panic, + * we expect other cores to be shutting down while we're + * executing rddm transfer. After returning from this function, + * we expect device to reset. + */ + + /* Trigger device into RDDM */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "pm_state:0x%x mhi_state:%s\n", + mhi_dev_ctxt->mhi_pm_state, + TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Register access not allowed\n"); + return -EIO; + } + + /* + * Normally we only set mhi_pm_state after grabbing pm_xfer_lock as a + * write, by function mhi_tryset_pm_state. Since we're in a kernel + * panic, we will set pm state w/o grabbing xfer lock. We're setting + * pm_state to LD as a safety precautions. If another core in middle + * of register access this should deter it. However, there is no + * no gurantee change will take effect. + */ + mhi_dev_ctxt->mhi_pm_state = MHI_PM_LD_ERR_FATAL_DETECT; + /* change should take effect immediately */ + smp_wmb(); + + bhie_mem_info = &rddm_table-> + bhie_mem_info[rddm_table->segment_count - 1]; + rx_sequence = rddm_table->sequence++; + + /* program the vector table */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Programming RXVEC table\n"); + val = HIGH_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, + BHIE_RXVECADDR_HIGH_OFFS, val); + val = LOW_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECADDR_LOW_OFFS, + val); + val = (u32)bhie_mem_info->size; + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECSIZE_OFFS, + val); + mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECDB_OFFS, + BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT, + rx_sequence); + + /* trigger device into rddm */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Triggering Device into RDDM mode\n"); + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR); + i = 0; + + while (timeout--) { + cur_exec = mhi_reg_read(bhi_ctxt->bhi_base, BHI_EXECENV); + state = mhi_get_m_state(mhi_dev_ctxt); + rx_status = mhi_reg_read(bhi_ctxt->bhi_base, + BHIE_RXVECSTATUS_OFFS); + /* if reg. values changed or each sec (udelay(1000)) log it */ + if (cur_exec != prev_exec || state != prev_state || + rx_status != prev_status || !(i & (SZ_1K - 1))) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "EXECENV:0x%x MHISTATE:0x%x RXSTATUS:0x%x\n", + cur_exec, state, rx_status); + prev_exec = cur_exec; + prev_state = state; + prev_status = rx_status; + }; + current_seq = (rx_status & BHIE_TXVECSTATUS_SEQNUM_BMSK) >> + BHIE_TXVECSTATUS_SEQNUM_SHFT; + rx_status = (rx_status & BHIE_TXVECSTATUS_STATUS_BMSK) >> + BHIE_TXVECSTATUS_STATUS_SHFT; + + if ((rx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) && + (current_seq == rx_sequence)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "rddm transfer completed\n"); + return 0; + } + udelay(BHIE_RDDM_DELAY_TIME_US); + i++; + } + + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "rddm transfer timeout\n"); + + return -EIO; } static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -439,7 +530,6 @@ void bhi_firmware_download(struct work_struct *work) struct bhi_ctxt_t *bhi_ctxt; struct bhie_mem_info mem_info; int ret; - long timeout; mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt, bhi_ctxt.fw_load_work); @@ -448,7 +538,14 @@ void bhi_firmware_download(struct work_struct *work) mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n"); wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_BHI); + mhi_dev_ctxt->mhi_state == MHI_STATE_BHI || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT); + if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT || + mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "MHI is not in valid state for firmware download\n"); + return; + } /* PBL image is the first segment in firmware vector table */ mem_info = *bhi_ctxt->fw_table.bhie_mem_info; @@ -462,10 +559,12 @@ void bhi_firmware_download(struct work_struct *work) mhi_init_state_transition(mhi_dev_ctxt, STATE_TRANSITION_RESET); - timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, - mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE, - msecs_to_jiffies(bhi_ctxt->poll_timeout)); - if (!timeout) { + wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(bhi_ctxt->poll_timeout)); + if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT || + mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_BHIE) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to Enter EXEC_ENV_BHIE\n"); return; diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h index 8f7b3d69347c..8f9bc52bbbe0 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.h +++ b/drivers/platform/msm/mhi/mhi_bhi.h @@ -87,6 +87,7 @@ #define BHI_POLL_SLEEP_TIME_MS 100 #define BHI_POLL_TIMEOUT_MS 2000 +#define BHIE_RDDM_DELAY_TIME_US (1000) int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt); void bhi_firmware_download(struct work_struct *work); diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c index caa34eadf8ea..ad9a6fd6b278 100644 --- a/drivers/platform/msm/mhi/mhi_pm.c +++ b/drivers/platform/msm/mhi/mhi_pm.c @@ -22,6 +22,22 @@ #include "mhi_hwio.h" #include "mhi_bhi.h" +static const char *const mhi_dev_ctrl_str[MHI_DEV_CTRL_MAXCMD] = { + [MHI_DEV_CTRL_INIT] = "INIT", + [MHI_DEV_CTRL_DE_INIT] = "DE-INIT", + [MHI_DEV_CTRL_SUSPEND] = "SUSPEND", + [MHI_DEV_CTRL_RESUME] = "RESUME", + [MHI_DEV_CTRL_POWER_OFF] = "OFF", + [MHI_DEV_CTRL_POWER_ON] = "ON", + [MHI_DEV_CTRL_TRIGGER_RDDM] = "TRIGGER RDDM", + [MHI_DEV_CTRL_RDDM] = "RDDM", + [MHI_DEV_CTRL_RDDM_KERNEL_PANIC] = "RDDM IN PANIC", + [MHI_DEV_CTRL_NOTIFY_LINK_ERROR] = "LD", +}; + +#define TO_MHI_DEV_CTRL_STR(cmd) ((cmd >= MHI_DEV_CTRL_MAXCMD) ? "INVALID" : \ + mhi_dev_ctrl_str[cmd]) + /* Write only sysfs attributes */ static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0); static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3); @@ -97,12 +113,14 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || - mhi_dev_ctxt->mhi_state == MHI_STATE_M1, - msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "Failed to get M0||M1 event, timeout, current state:%s\n", + "Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n", + mhi_dev_ctxt->mhi_pm_state, TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); return -EIO; } @@ -122,9 +140,10 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt, write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n"); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M3, - msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M3 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to get M3 event, timeout, current state:%s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); @@ -158,12 +177,13 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || - mhi_dev_ctxt->mhi_state == MHI_STATE_M1, - msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "Failed to get M0 event, timeout\n"); + "Failed to get M0 event, timeout or LD\n"); r = -EIO; } else r = 0; @@ -295,13 +315,16 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete, - msecs_to_jiffies(timeout)); - if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS) + wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete, + msecs_to_jiffies(timeout)); + if (mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS) ret_val = -EIO; else ret_val = 0; + /* wait for firmware download to complete */ + flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work); + if (ret_val) { read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); @@ -537,16 +560,16 @@ void mhi_link_state_cb(struct msm_pcie_notify *notify) } } -int mhi_pm_control_device(struct mhi_device *mhi_device, - enum mhi_dev_ctrl ctrl) +int mhi_pm_control_device(struct mhi_device *mhi_device, enum mhi_dev_ctrl ctrl) { struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt; + unsigned long flags; if (!mhi_dev_ctxt) return -EINVAL; - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Entered with cmd:%d\n", ctrl); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered with cmd:%s\n", + TO_MHI_DEV_CTRL_STR(ctrl)); switch (ctrl) { case MHI_DEV_CTRL_INIT: @@ -560,12 +583,46 @@ int mhi_pm_control_device(struct mhi_device *mhi_device, case MHI_DEV_CTRL_POWER_OFF: mhi_pm_slave_mode_power_off(mhi_dev_ctxt); break; + case MHI_DEV_CTRL_TRIGGER_RDDM: + write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, + flags); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "failed to trigger rddm, no register access in state:0x%x\n", + mhi_dev_ctxt->mhi_pm_state); + return -EIO; + } + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR); + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); + break; case MHI_DEV_CTRL_RDDM: return bhi_rddm(mhi_dev_ctxt, false); + case MHI_DEV_CTRL_RDDM_KERNEL_PANIC: + return bhi_rddm(mhi_dev_ctxt, true); case MHI_DEV_CTRL_DE_INIT: - if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) + if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) { + enum MHI_PM_STATE cur_state; + /* + * If bus master calls DE_INIT before calling POWER_OFF + * means a critical failure occurred during POWER_ON + * state transition and external PCIe device may not + * respond to host. Force PM state to PCIe linkdown + * state prior to starting shutdown process to avoid + * accessing PCIe link. + */ + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, + MHI_PM_LD_ERR_FATAL_DETECT); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to transition to state 0x%x from 0x%x\n", + MHI_PM_LD_ERR_FATAL_DETECT, cur_state); + } process_disable_transition(MHI_PM_SHUTDOWN_PROCESS, mhi_dev_ctxt); + } bhi_exit(mhi_dev_ctxt); break; case MHI_DEV_CTRL_NOTIFY_LINK_ERROR: @@ -580,6 +637,12 @@ int mhi_pm_control_device(struct mhi_device *mhi_device, mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Failed to transition to state 0x%x from 0x%x\n", MHI_PM_LD_ERR_FATAL_DETECT, cur_state); + + /* wake up all threads that's waiting for state change events */ + complete(&mhi_dev_ctxt->cmd_complete); + wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event); + wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event); + wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event); break; } default: diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c index 2906393cbd5c..ea2a91bd2d06 100644 --- a/drivers/platform/msm/mhi/mhi_states.c +++ b/drivers/platform/msm/mhi/mhi_states.c @@ -147,7 +147,8 @@ void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt, * M1 -> M3_ENTER --> M3 * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR * L2: SHUTDOWN_PROCESS -> DISABLE -> SSR_PENDING (via SSR Notification only) - * L3: LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS + * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT + * LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS */ static const struct mhi_pm_transitions const mhi_state_transitions[] = { /* L0 States */ @@ -216,7 +217,7 @@ static const struct mhi_pm_transitions const mhi_state_transitions[] = { /* L3 States */ { MHI_PM_LD_ERR_FATAL_DETECT, - MHI_PM_SHUTDOWN_PROCESS + MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_PROCESS }, /* From SSR notification only */ { diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c index ccef04ae9eb2..51b6d63fe994 100644 --- a/drivers/power/qcom/debug_core.c +++ b/drivers/power/qcom/debug_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -288,8 +288,8 @@ static const struct file_operations msm_core_ptable_ops = { int msm_core_debug_init(void) { - struct dentry *dir; - struct dentry *file; + struct dentry *dir = NULL; + struct dentry *file = NULL; int i; msm_core_data = get_cpu_pwr_stats(); diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 36ac1960a176..bd7231705319 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -491,7 +491,7 @@ static void fg_encode_default(struct fg_sram_param *sp, int i, mask = 0xff; int64_t temp; - temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr); + temp = (int64_t)div_s64((s64)val * sp[id].numrtr, sp[id].denmtr); pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); for (i = 0; i < sp[id].len; i++) { buf[i] = temp & mask; @@ -1320,9 +1320,16 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return rc; } - cc_soc_delta_pct = DIV_ROUND_CLOSEST( - abs(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, - CC_SOC_30BIT); + cc_soc_delta_pct = + div64_s64((int64_t)(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, + CC_SOC_30BIT); + + /* If the delta is < 50%, then skip processing full data */ + if (cc_soc_delta_pct < 50) { + pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct); + return -ERANGE; + } + delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_pct, 100); chip->cl.final_cc_uah = chip->cl.init_cc_uah + delta_cc_uah; @@ -1392,7 +1399,6 @@ out: return rc; } -#define FULL_SOC_RAW 255 static void fg_cap_learning_update(struct fg_chip *chip) { int rc, batt_soc, batt_soc_msb; diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 9a20b6925877..21381d1ad7fa 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -1505,7 +1505,7 @@ static int cpr3_hmss_init_regulator(struct cpr3_regulator *vreg) static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl) { struct cpr3_msm8996_hmss_fuses *fuse = NULL; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; u32 aging_ro_scale; int i, j, rc; @@ -1710,7 +1710,7 @@ static int cpr3_hmss_regulator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *match; struct cpr3_controller *ctrl; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; int i, j, rc; if (!dev->of_node) { diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index d131a8ea4144..3adbd41ede88 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -3117,7 +3117,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) struct cpr4_sdelta *sdelta; bool valid = false; bool thread_valid; - int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt; + int i, j, rc; + int new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt = 0; u32 reg_last_measurement = 0, sdelta_size; int *sdelta_table, *boost_table; diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index 2307da9497dc..ecf7885a4bff 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -1975,7 +1975,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) static int cprh_kbss_init_aging(struct cpr3_controller *ctrl) { struct cprh_kbss_fuses *fuse = NULL; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; u32 aging_ro_scale; int i, j, rc = 0; diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c index d26fd3bea788..a5dc1b983bcb 100644 --- a/drivers/regulator/rpm-smd-regulator.c +++ b/drivers/regulator/rpm-smd-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -207,6 +207,7 @@ struct rpm_regulator { bool use_pin_ctrl_for_enable; struct rpm_vreg_request req; int system_load; + int hpm_threshold_current; int min_uV; int max_uV; u32 pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_COUNT]; @@ -1030,6 +1031,34 @@ static unsigned int rpm_vreg_get_bob_mode(struct regulator_dev *rdev) return mode; } +static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + u32 mode = REGULATOR_MODE_NORMAL; + + if (reg->hpm_threshold_current > 0) { + if (load_uA >= reg->hpm_threshold_current) { + /* PWM mode */ + mode = (reg->rpm_vreg->regulator_type + == RPM_REGULATOR_TYPE_BOB) + ? REGULATOR_MODE_FAST + : REGULATOR_MODE_NORMAL; + } else { + /* AUTO mode */ + mode = (reg->rpm_vreg->regulator_type + == RPM_REGULATOR_TYPE_BOB) + ? REGULATOR_MODE_NORMAL + : REGULATOR_MODE_IDLE; + } + } else { + /* Default to the current mode if no threshold is present. */ + mode = reg->rdesc.ops->get_mode(rdev); + } + + return mode; +} + static int rpm_vreg_enable_time(struct regulator_dev *rdev) { struct rpm_regulator *reg = rdev_get_drvdata(rdev); @@ -1402,6 +1431,18 @@ static struct regulator_ops smps_ops = { .enable_time = rpm_vreg_enable_time, }; +static struct regulator_ops smps_optimum_mode_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + static struct regulator_ops switch_ops = { .enable = rpm_vreg_enable, .disable = rpm_vreg_disable, @@ -1426,6 +1467,7 @@ static struct regulator_ops bob_ops = { .get_voltage = rpm_vreg_get_voltage, .set_mode = rpm_vreg_set_bob_mode, .get_mode = rpm_vreg_get_bob_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, .enable_time = rpm_vreg_enable_time, }; @@ -1676,6 +1718,12 @@ static int rpm_vreg_device_probe(struct platform_device *pdev) if (of_get_property(node, "parent-supply", NULL)) init_data->supply_regulator = "parent"; + of_property_read_u32(node, "qcom,pwm-threshold-current", + ®->hpm_threshold_current); + if (reg->hpm_threshold_current > 0 + && regulator_type == RPM_REGULATOR_TYPE_SMPS) + reg->rdesc.ops = &smps_optimum_mode_ops; + /* * Fill in ops and mode masks based on callbacks specified for * this type of regulator. @@ -1689,8 +1737,13 @@ static int rpm_vreg_device_probe(struct platform_device *pdev) if (reg->rdesc.ops->get_mode) { init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS; - init_data->constraints.valid_modes_mask - |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + + if (regulator_type == RPM_REGULATOR_TYPE_BOB) + init_data->constraints.valid_modes_mask + = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL; + else + init_data->constraints.valid_modes_mask + |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; } reg->rdesc.name = init_data->constraints.name; diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index 484f0412addf..d469463a0b00 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -49,6 +49,7 @@ enum qpnp_regulator_uniq_type { QPNP_TYPE_HF, QPNP_TYPE_FTS2, QPNP_TYPE_FTS2p5, + QPNP_TYPE_FTS426, QPNP_TYPE_ULT_HF, }; @@ -56,6 +57,7 @@ enum qpnp_regulator_type { QPNP_HF_TYPE = 0x03, QPNP_FTS2_TYPE = 0x1C, QPNP_FTS2p5_TYPE = 0x1C, + QPNP_FTS426_TYPE = 0x1C, QPNP_ULT_HF_TYPE = 0x22, }; @@ -63,15 +65,22 @@ enum qpnp_regulator_subtype { QPNP_FTS2_SUBTYPE = 0x08, QPNP_HF_SUBTYPE = 0x08, QPNP_FTS2p5_SUBTYPE = 0x09, + QPNP_FTS426_SUBTYPE = 0x0A, QPNP_ULT_HF_SUBTYPE = 0x0D, }; +enum qpnp_logical_mode { + QPNP_LOGICAL_MODE_AUTO, + QPNP_LOGICAL_MODE_PWM, +}; + static const struct voltage_range fts2_range0 = {0, 350000, 1275000, 5000}; static const struct voltage_range fts2_range1 = {0, 700000, 2040000, 10000}; static const struct voltage_range fts2p5_range0 = { 80000, 350000, 1355000, 5000}; static const struct voltage_range fts2p5_range1 = {160000, 700000, 2200000, 10000}; +static const struct voltage_range fts426_range = {0, 320000, 1352000, 4000}; static const struct voltage_range ult_hf_range0 = {375000, 375000, 1562500, 12500}; static const struct voltage_range ult_hf_range1 = {750000, 750000, 1525000, @@ -86,17 +95,45 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41 #define QPNP_SMPS_REG_MODE 0x45 #define QPNP_SMPS_REG_STEP_CTRL 0x61 +#define QPNP_SMPS_REG_UL_LL_CTRL 0x68 + +/* FTS426 voltage control registers */ +#define QPNP_FTS426_REG_VOLTAGE_LB 0x40 +#define QPNP_FTS426_REG_VOLTAGE_UB 0x41 +#define QPNP_FTS426_REG_VOLTAGE_VALID_LB 0x42 +#define QPNP_FTS426_REG_VOLTAGE_VALID_UB 0x43 + +/* HF voltage limit registers */ +#define QPNP_HF_REG_VOLTAGE_ULS 0x69 +#define QPNP_HF_REG_VOLTAGE_LLS 0x6B + +/* FTS voltage limit registers */ +#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A +#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C + +/* FTS426 voltage limit registers */ +#define QPNP_FTS426_REG_VOLTAGE_ULS_LB 0x68 +#define QPNP_FTS426_REG_VOLTAGE_ULS_UB 0x69 + +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 #define QPNP_SMPS_MODE_PWM 0x80 #define QPNP_SMPS_MODE_AUTO 0x40 +#define QPNP_FTS426_MODE_PWM 0x07 +#define QPNP_FTS426_MODE_AUTO 0x06 #define QPNP_SMPS_STEP_CTRL_STEP_MASK 0x18 #define QPNP_SMPS_STEP_CTRL_STEP_SHIFT 3 #define QPNP_SMPS_STEP_CTRL_DELAY_MASK 0x07 #define QPNP_SMPS_STEP_CTRL_DELAY_SHIFT 0 +#define QPNP_FTS426_STEP_CTRL_DELAY_MASK 0x03 +#define QPNP_FTS426_STEP_CTRL_DELAY_SHIFT 0 /* Clock rate in kHz of the FTS2 regulator reference clock. */ #define QPNP_SMPS_CLOCK_RATE 19200 +#define QPNP_FTS426_CLOCK_RATE 4800 /* Time to delay in us to ensure that a mode change has completed. */ #define QPNP_FTS2_MODE_CHANGE_DELAY 50 @@ -107,6 +144,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, /* Minimum voltage stepper delay for each step. */ #define QPNP_FTS2_STEP_DELAY 8 #define QPNP_HF_STEP_DELAY 20 +#define QPNP_FTS426_STEP_DELAY 2 /* Arbitrarily large max step size used to avoid possible numerical overflow */ #define SPM_REGULATOR_MAX_STEP_UV 10000000 @@ -117,6 +155,8 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, */ #define QPNP_FTS2_STEP_MARGIN_NUM 4 #define QPNP_FTS2_STEP_MARGIN_DEN 5 +#define QPNP_FTS426_STEP_MARGIN_NUM 10 +#define QPNP_FTS426_STEP_MARGIN_DEN 11 /* * Settling delay for FTS2.5 @@ -140,8 +180,8 @@ struct spm_vreg { u32 max_step_uV; bool online; u16 spmi_base_addr; - u8 init_mode; - u8 mode; + enum qpnp_logical_mode init_mode; + enum qpnp_logical_mode mode; int step_rate; enum qpnp_regulator_uniq_type regulator_type; u32 cpu_num; @@ -163,6 +203,9 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV) { int vlevel; + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return roundup(uV, vreg->range->step_uV) / 1000; + vlevel = DIV_ROUND_UP(uV - vreg->range->min_uV, vreg->range->step_uV); /* Fix VSET for ULT HF Buck */ @@ -177,6 +220,8 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV) static int spm_regulator_vlevel_to_uv(struct spm_vreg *vreg, int vlevel) { + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return vlevel * 1000; /* * Calculate ULT HF buck VSET based on range: * In case of range 0: VSET is a 7 bit value. @@ -204,32 +249,91 @@ static unsigned spm_regulator_vlevel_to_selector(struct spm_vreg *vreg, static int qpnp_smps_read_voltage(struct spm_vreg *vreg) { int rc; - u8 reg = 0; - uint val; + u8 val[2] = {0}; - rc = regmap_read(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, - &val); - if (rc) { - dev_err(&vreg->pdev->dev, - "%s: could not read voltage setpoint register, rc=%d\n", - __func__, rc); - return rc; + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_VALID_LB, + val, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint registers, rc=%d\n", + __func__, rc); + return rc; + } + + vreg->last_set_vlevel = ((unsigned)val[1] << 8) | val[0]; + } else { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + val, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint register, rc=%d\n", + __func__, rc); + return rc; + } + vreg->last_set_vlevel = val[0]; } - reg = (u8)val; - vreg->last_set_vlevel = reg; - vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, reg); + vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, + vreg->last_set_vlevel); + return rc; +} + +static int qpnp_smps_write_voltage(struct spm_vreg *vreg, unsigned vlevel) +{ + int rc = 0; + u8 reg[2]; + + /* Set voltage control registers via SPMI. */ + reg[0] = vlevel & 0xFF; + reg[1] = (vlevel >> 8) & 0xFF; + + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + rc = regmap_bulk_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_LB, + reg, 2); + } else { + rc = regmap_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + reg[0]); + } + + if (rc) + pr_err("%s: regmap_write failed, rc=%d\n", + vreg->rdesc.name, rc); return rc; } +static inline enum qpnp_logical_mode qpnp_regval_to_mode(struct spm_vreg *vreg, + u8 regval) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return (regval == QPNP_FTS426_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; + else + return (regval & QPNP_SMPS_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; +} + +static inline u8 qpnp_mode_to_regval(struct spm_vreg *vreg, + enum qpnp_logical_mode mode) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_FTS426_MODE_PWM : QPNP_FTS426_MODE_AUTO; + else + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_SMPS_MODE_PWM : QPNP_SMPS_MODE_AUTO; +} + static int qpnp_smps_set_mode(struct spm_vreg *vreg, u8 mode) { int rc; rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, mode); + vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, + qpnp_mode_to_regval(vreg, mode)); if (rc) dev_err(&vreg->pdev->dev, "%s: could not write to mode register, rc=%d\n", @@ -275,7 +379,6 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) bool spm_failed = false; int rc = 0; u32 slew_delay; - u8 reg; if (likely(!vreg->bypass_spm)) { /* Set voltage control register via SPM. */ @@ -288,13 +391,9 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) } if (unlikely(vreg->bypass_spm || spm_failed)) { - /* Set voltage control register via SPMI. */ - reg = vlevel; - rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, - reg); + rc = qpnp_smps_write_voltage(vreg, vlevel); if (rc) { - pr_err("%s: regmap_write failed, rc=%d\n", + pr_err("%s: voltage write failed, rc=%d\n", vreg->rdesc.name, rc); return rc; } @@ -351,12 +450,12 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev) return 0; pwm_required = (vreg->regulator_type == QPNP_TYPE_FTS2) - && !(vreg->init_mode & QPNP_SMPS_MODE_PWM) + && (vreg->init_mode != QPNP_LOGICAL_MODE_PWM) && vreg->uV > vreg->last_set_uV; if (pwm_required) { /* Switch to PWM mode so that voltage ramping is fast. */ - rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_PWM); + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_PWM); if (rc) return rc; } @@ -375,7 +474,7 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev) /* Wait for mode transition to complete. */ udelay(QPNP_FTS2_MODE_CHANGE_DELAY - QPNP_SPMI_WRITE_MIN_DELAY); /* Switch to AUTO mode so that power consumption is lowered. */ - rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_AUTO); + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_AUTO); if (rc) return rc; } @@ -466,7 +565,7 @@ static unsigned int spm_regulator_get_mode(struct regulator_dev *rdev) { struct spm_vreg *vreg = rdev_get_drvdata(rdev); - return vreg->mode == QPNP_SMPS_MODE_PWM + return vreg->mode == QPNP_LOGICAL_MODE_PWM ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; } @@ -480,8 +579,8 @@ static int spm_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) * in the case that qcom,mode has been specified as "pwm" in device * tree. */ - vreg->mode - = mode == REGULATOR_MODE_NORMAL ? QPNP_SMPS_MODE_PWM : vreg->init_mode; + vreg->mode = (mode == REGULATOR_MODE_NORMAL) ? QPNP_LOGICAL_MODE_PWM + : vreg->init_mode; return qpnp_smps_set_mode(vreg, vreg->mode); } @@ -538,7 +637,7 @@ static int spm_regulator_avs_set_voltage(struct regulator_dev *rdev, int min_uV, return -EINVAL; } - vlevel_max = (uV - range->min_uV) / range->step_uV; + vlevel_max = spm_regulator_uv_to_vlevel(vreg, uV); avs_max_uV = spm_regulator_vlevel_to_uv(vreg, vlevel_max); if (avs_max_uV < min_uV) { @@ -646,6 +745,9 @@ static int qpnp_smps_check_type(struct spm_vreg *vreg) } else if (type[0] == QPNP_FTS2p5_TYPE && type[1] == QPNP_FTS2p5_SUBTYPE) { vreg->regulator_type = QPNP_TYPE_FTS2p5; + } else if (type[0] == QPNP_FTS426_TYPE + && type[1] == QPNP_FTS426_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_FTS426; } else if (type[0] == QPNP_ULT_HF_TYPE && type[1] == QPNP_ULT_HF_SUBTYPE) { vreg->regulator_type = QPNP_TYPE_ULT_HF; @@ -750,10 +852,10 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) &mode_name); if (!rc) { if (strcmp("pwm", mode_name) == 0) { - vreg->init_mode = QPNP_SMPS_MODE_PWM; + vreg->init_mode = QPNP_LOGICAL_MODE_PWM; } else if ((strcmp("auto", mode_name) == 0) && (vreg->regulator_type != QPNP_TYPE_ULT_HF)) { - vreg->init_mode = QPNP_SMPS_MODE_AUTO; + vreg->init_mode = QPNP_LOGICAL_MODE_AUTO; } else { dev_err(&vreg->pdev->dev, "%s: unknown regulator mode: %s\n", @@ -761,13 +863,9 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) return -EINVAL; } - rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, - *&vreg->init_mode); + rc = qpnp_smps_set_mode(vreg, vreg->init_mode); if (rc) - dev_err(&vreg->pdev->dev, - "%s: could not write mode register, rc=%d\n", - __func__, rc); + return rc; } else { rc = regmap_read(vreg->regmap, vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, @@ -776,7 +874,7 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) dev_err(&vreg->pdev->dev, "%s: could not read mode register, rc=%d\n", __func__, rc); - vreg->init_mode = (u8)val; + vreg->init_mode = qpnp_regval_to_mode(vreg, val); } vreg->mode = vreg->init_mode; @@ -801,26 +899,41 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) } reg = (u8)val; - /* ULT buck does not support steps */ - if (vreg->regulator_type != QPNP_TYPE_ULT_HF) + /* ULT and FTS426 bucks do not support steps */ + if (vreg->regulator_type != QPNP_TYPE_ULT_HF && vreg->regulator_type != + QPNP_TYPE_FTS426) step = (reg & QPNP_SMPS_STEP_CTRL_STEP_MASK) >> QPNP_SMPS_STEP_CTRL_STEP_SHIFT; - delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK) - >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT; + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + delay = (reg & QPNP_FTS426_STEP_CTRL_DELAY_MASK) + >> QPNP_FTS426_STEP_CTRL_DELAY_SHIFT; - /* step_rate has units of uV/us. */ - vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV - * (1 << step); + /* step_rate has units of uV/us. */ + vreg->step_rate = QPNP_FTS426_CLOCK_RATE * vreg->range->step_uV; + } else { + delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK) + >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT; + + /* step_rate has units of uV/us. */ + vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV + * (1 << step); + } if ((vreg->regulator_type == QPNP_TYPE_ULT_HF) || (vreg->regulator_type == QPNP_TYPE_HF)) vreg->step_rate /= 1000 * (QPNP_HF_STEP_DELAY << delay); + else if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->step_rate /= 1000 * (QPNP_FTS426_STEP_DELAY << delay); else vreg->step_rate /= 1000 * (QPNP_FTS2_STEP_DELAY << delay); - vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM - / QPNP_FTS2_STEP_MARGIN_DEN; + if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->step_rate = vreg->step_rate * QPNP_FTS426_STEP_MARGIN_NUM + / QPNP_FTS426_STEP_MARGIN_DEN; + else + vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM + / QPNP_FTS2_STEP_MARGIN_DEN; /* Ensure that the stepping rate is greater than 0. */ vreg->step_rate = max(vreg->step_rate, 1); @@ -828,10 +941,93 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) return rc; } +static int qpnp_smps_check_constraints(struct spm_vreg *vreg, + struct regulator_init_data *init_data) +{ + int rc = 0, limit_min_uV, limit_max_uV; + u16 ul_reg, ll_reg; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID; + ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID; + + switch (vreg->regulator_type) { + case QPNP_TYPE_HF: + ul_reg = QPNP_HF_REG_VOLTAGE_ULS; + ll_reg = QPNP_HF_REG_VOLTAGE_LLS; + case QPNP_TYPE_FTS2: + case QPNP_TYPE_FTS2p5: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ul_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ll_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + break; + case QPNP_TYPE_FTS426: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_FTS426_REG_VOLTAGE_ULS_LB, + reg, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, + ((unsigned)reg[1] << 8) | reg[0]); + break; + case QPNP_TYPE_ULT_HF: + /* no HW voltage limit configuration */ + break; + } + + if (init_data->constraints.min_uV < limit_min_uV + || init_data->constraints.max_uV > limit_max_uV) { + dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + init_data->constraints.min_uV, + init_data->constraints.max_uV, limit_min_uV, + limit_max_uV); + return -EINVAL; + } + + return rc; +} + static bool spm_regulator_using_range0(struct spm_vreg *vreg) { return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0 - || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0; + || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0 + || vreg->range == &fts426_range; } /* Register a regulator to enable/disable AVS and set AVS min/max limits. */ @@ -969,6 +1165,8 @@ static int spm_regulator_probe(struct platform_device *pdev) rc = qpnp_smps_init_range(vreg, &fts2_range0, &fts2_range1); else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) rc = qpnp_smps_init_range(vreg, &fts2p5_range0, &fts2p5_range1); + else if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->range = &fts426_range; else if (vreg->regulator_type == QPNP_TYPE_HF) rc = qpnp_smps_init_range(vreg, &hf_range0, &hf_range1); else if (vreg->regulator_type == QPNP_TYPE_ULT_HF) @@ -1006,6 +1204,13 @@ static int spm_regulator_probe(struct platform_device *pdev) return -EINVAL; } + rc = qpnp_smps_check_constraints(vreg, init_data); + if (rc) { + dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n", + __func__, rc); + return rc; + } + vreg->rdesc.name = init_data->constraints.name; vreg->rdesc.type = REGULATOR_VOLTAGE; vreg->rdesc.owner = THIS_MODULE; @@ -1050,8 +1255,8 @@ static int spm_regulator_probe(struct platform_device *pdev) vreg->rdesc.name, spm_regulator_using_range0(vreg) ? "LV" : "MV", vreg->uV, - vreg->init_mode & QPNP_SMPS_MODE_PWM ? "PWM" : - (vreg->init_mode & QPNP_SMPS_MODE_AUTO ? "AUTO" : "PFM"), + vreg->init_mode == QPNP_LOGICAL_MODE_PWM ? "PWM" : + (vreg->init_mode == QPNP_LOGICAL_MODE_AUTO ? "AUTO" : "PFM"), vreg->step_rate); return rc; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a90c51a113d2..b8dc7e834cf3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3285,8 +3285,10 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, /* * May get invoked from shutdown and IOCTL contexts. * In shutdown context, it comes in with lock acquired. + * In error recovery context, it may come with lock acquired. */ - if (!ufshcd_is_shutdown_ongoing(hba)) + + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) down_read(&hba->lock); /* @@ -3320,7 +3322,7 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, out_put_tag: ufshcd_put_dev_cmd_tag(hba, tag); wake_up(&hba->dev_cmd.tag_wq); - if (!ufshcd_is_shutdown_ongoing(hba)) + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) up_read(&hba->lock); return err; } diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 168db46084df..8385987e8888 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -849,7 +849,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) rcu_id = srcu_read_lock(&einfo->use_ref); - if (unlikely(!einfo->rx_fifo)) { + if (unlikely(!einfo->rx_fifo) && atomic_ctx) { if (!get_rx_fifo(einfo)) { srcu_read_unlock(&einfo->use_ref, rcu_id); return; diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rules.c b/drivers/soc/qcom/msm_bus/msm_bus_rules.c index ea29e303bbde..43a892bbd27c 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_rules.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_rules.c @@ -597,7 +597,7 @@ void msm_rule_register(int num_rules, struct bus_rule_type *rule, static bool __rule_unregister(int num_rules, struct bus_rule_type *rule, struct notifier_block *nb) { - int i; + int i = 0; struct rule_node_info *node = NULL; struct rule_node_info *node_tmp = NULL; struct rules_def *node_rule; diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index 991bce363740..f85c4ba06b47 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1113,6 +1113,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) rc = PTR_ERR(d->subsys); goto err_subsys; } + d->desc.subsys_dev = d->subsys; return 0; err_subsys: diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index e72663bd2138..14c13db991a1 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -540,7 +540,7 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, { struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); int ret = 0; - int val; + int val = 0; u8 *reg_val = (u8 *)buf; if (!swrm) { diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 205af6627b80..1de3dddfd0f4 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -146,7 +146,7 @@ int adjust_minadj(short *min_score_adj) static int lmk_vmpressure_notifier(struct notifier_block *nb, unsigned long action, void *data) { - int other_free, other_file; + int other_free = 0, other_file = 0; unsigned long pressure = action; int array_size = ARRAY_SIZE(lowmem_adj); diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index d59b9736c570..5f955af4671a 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -2119,7 +2119,7 @@ static int get_device_tree_data(struct platform_device *pdev, { struct device_node *of_node = pdev->dev.of_node; struct resource *res_mem = NULL; - u32 *tsens_slope_data, *sensor_id, *client_id; + u32 *tsens_slope_data = NULL, *sensor_id, *client_id; u32 *temp1_calib_offset_factor, *temp2_calib_offset_factor; u32 rc = 0, i, tsens_num_sensors = 0; u32 cycle_monitor = 0, wd_bark = 0; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d885033d3322..9dbd7595a7a3 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -376,10 +376,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) int i; ret = 0; - virt_dev = xhci->devs[slot_id]; - if (!virt_dev) - return -ENODEV; - cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); if (!cmd) { xhci_dbg(xhci, "Couldn't allocate command structure.\n"); @@ -387,6 +383,13 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) } spin_lock_irqsave(&xhci->lock, flags); + virt_dev = xhci->devs[slot_id]; + if (!virt_dev) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cmd); + return -ENODEV; + } + for (i = LAST_EP_INDEX; i > 0; i--) { if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { struct xhci_command *command; @@ -403,6 +406,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) i, suspend); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, command); goto err_cmd_queue; } } diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index d46e21bd2c68..7a250c31f44d 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -29,6 +29,11 @@ #include <linux/usb/usbpd.h> #include "usbpd.h" +/* To start USB stack for USB3.1 complaince testing */ +static bool usb_compliance_mode; +module_param(usb_compliance_mode, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usb_compliance_mode, "Start USB stack for USB3.1 compliance testing"); + enum usbpd_state { PE_UNKNOWN, PE_ERROR_RECOVERY, @@ -483,13 +488,12 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15); /* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */ - /* MessageID incremented regardless of Tx error */ - pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; - if (ret < 0) return ret; else if (ret != num_data * sizeof(u32)) return -EIO; + + pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; return 0; } @@ -581,6 +585,8 @@ static int pd_eval_src_caps(struct usbpd *pd) static void pd_send_hard_reset(struct usbpd *pd) { + union power_supply_propval val = {0}; + usbpd_dbg(&pd->dev, "send hard reset"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ @@ -588,6 +594,7 @@ static void pd_send_hard_reset(struct usbpd *pd) pd->hard_reset_count++; pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ pd->in_pr_swap = false; + power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } static void kick_sm(struct usbpd *pd, int ms) @@ -603,6 +610,8 @@ static void kick_sm(struct usbpd *pd, int ms) static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) { + union power_supply_propval val = {1}; + if (type != HARD_RESET_SIG) { usbpd_err(&pd->dev, "invalid signal (%d) received\n", type); return; @@ -613,6 +622,9 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_recvd = true; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + kick_sm(pd, 0); } @@ -778,6 +790,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) if (pd->in_pr_swap) { kick_sm(pd, SWAP_SOURCE_START_TIME); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); break; } @@ -890,7 +905,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_dr = DR_UFP; if (pd->psy_type == POWER_SUPPLY_TYPE_USB || - pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) + pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP || + usb_compliance_mode) start_usb_peripheral(pd); } @@ -1566,7 +1582,6 @@ static void usbpd_sm(struct work_struct *w) memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); - val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -1597,6 +1612,10 @@ static void usbpd_sm(struct work_struct *w) usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC, (ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC); + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + /* set due to dual_role class "mode" change */ if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) val.intval = pd->forced_pr; @@ -1620,11 +1639,22 @@ static void usbpd_sm(struct work_struct *w) if (pd->hard_reset_recvd) { pd->hard_reset_recvd = false; - val.intval = 1; + if (pd->requested_current) { + val.intval = pd->requested_current = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); + } + + pd->requested_voltage = 5000000; + val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + pd->in_explicit_contract = false; pd->selected_pdo = pd->requested_pdo = 0; pd->rdo = 0; @@ -1885,6 +1915,9 @@ static void usbpd_sm(struct work_struct *w) case PE_SNK_WAIT_FOR_CAPABILITIES: pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; @@ -1904,15 +1937,6 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); - } else if (pd->pd_connected) { - usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n"); - - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, - &val); - - usbpd_set_state(pd, PE_ERROR_RECOVERY); } else { usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n"); @@ -2063,6 +2087,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { @@ -2206,6 +2233,9 @@ static void usbpd_sm(struct work_struct *w) case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); pd->in_explicit_contract = false; if (pd->vbus_enabled) { @@ -2246,6 +2276,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index fa3b71d6ce08..e200c25bc23a 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -108,6 +108,7 @@ struct usb_pdphy { int tx_status; u8 frame_filter_val; bool in_test_data_mode; + bool rx_busy; enum data_role data_role; enum power_role power_role; @@ -485,6 +486,12 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, return -EINVAL; } + ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1); + if (ret || val || pdphy->rx_busy) { + dev_err(pdphy->dev, "%s: RX message pending\n", __func__); + return -EBUSY; + } + pdphy->tx_status = -EINPROGRESS; /* write 2 byte SOP message header */ @@ -657,6 +664,15 @@ static int pd_phy_bist_mode(u8 bist_mode) BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE); } +static irqreturn_t pdphy_msg_rx_irq(int irq, void *data) +{ + struct usb_pdphy *pdphy = data; + + pdphy->rx_busy = true; + + return IRQ_WAKE_THREAD; +} + static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) { u8 size, rx_status, frame_type; @@ -713,6 +729,7 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) false); pdphy->rx_bytes += size + 1; done: + pdphy->rx_busy = false; return IRQ_HANDLED; } @@ -798,7 +815,7 @@ static int pdphy_probe(struct platform_device *pdev) return ret; ret = pdphy_request_irq(pdphy, pdev->dev.of_node, - &pdphy->msg_rx_irq, "msg-rx", NULL, + &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq, pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); if (ret < 0) return ret; diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index c082ae8e50ce..2a708dd2ecee 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1097,6 +1097,13 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, void __user *argp = (void __user *)arg; long ret = 0; + memset(&var, 0, sizeof(var)); + memset(&fix, 0, sizeof(fix)); + memset(&con2fb, 0, sizeof(con2fb)); + memset(&cmap_from, 0, sizeof(cmap_from)); + memset(&cmap, 0, sizeof(cmap)); + memset(&event, 0, sizeof(event)); + switch (cmd) { case FBIOGET_VSCREENINFO: if (!lock_fb_info(info)) diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c index 3330f8f62b78..c6ff92ed1686 100644 --- a/drivers/video/fbdev/msm/mdss_dba_utils.c +++ b/drivers/video/fbdev/msm/mdss_dba_utils.c @@ -576,7 +576,6 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) video_cfg.h_pulse_width = pinfo->lcdc.h_pulse_width; video_cfg.v_pulse_width = pinfo->lcdc.v_pulse_width; video_cfg.pclk_khz = (unsigned long)pinfo->clk_rate / 1000; - video_cfg.hdmi_mode = !hdmi_edid_is_dvi_mode(ud->edid_data); /* Calculate number of DSI lanes configured */ video_cfg.num_of_input_lanes = 0; @@ -592,6 +591,8 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) /* Get scan information from EDID */ video_cfg.vic = mdss_dba_get_vic_panel_info(ud, pinfo); ud->current_vic = video_cfg.vic; + video_cfg.hdmi_mode = hdmi_edid_get_sink_mode(ud->edid_data, + video_cfg.vic); video_cfg.scaninfo = hdmi_edid_get_sink_scaninfo(ud->edid_data, video_cfg.vic); if (ud->ops.video_on) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index 0d9cf7b72b4d..e8c3fe9f4957 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -311,7 +311,7 @@ static void mdss_dp_calc_tu_parameters(u8 link_rate, u8 ln_cnt, bool n_err_neg, nn_err_neg; u8 hblank_margin = 16; - u8 tu_size, tu_size_desired, tu_size_minus1; + u8 tu_size, tu_size_desired = 0, tu_size_minus1; int valid_boundary_link; u64 resulting_valid; u64 total_valid; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index d5caf0e6bb1c..4f1333426113 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -2091,7 +2091,7 @@ static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata, { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL; - struct mdss_panel_info *pinfo, *spinfo; + struct mdss_panel_info *pinfo, *spinfo = NULL; int rc = 0; if (pdata == NULL) { @@ -4301,6 +4301,15 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, of_property_read_bool(ctrl_pdev->dev.of_node, "qcom,platform-bklight-en-gpio-invert"); + ctrl_pdata->avdd_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-avdd-en-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + pr_info("%s: avdd_en gpio not specified\n", __func__); + + ctrl_pdata->avdd_en_gpio_invert = + of_property_read_bool(ctrl_pdev->dev.of_node, + "qcom,platform-avdd-en-gpio-invert"); + ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, "qcom,platform-reset-gpio", 0); if (!gpio_is_valid(ctrl_pdata->rst_gpio)) diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 62d88f0af652..7fabd4944cbd 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -455,6 +455,8 @@ struct mdss_dsi_ctrl_pdata { int bklt_en_gpio; bool bklt_en_gpio_invert; bool bklt_en_gpio_state; + int avdd_en_gpio; + bool avdd_en_gpio_invert; int lcd_mode_sel_gpio; int bklt_ctrl; /* backlight ctrl */ bool pwm_pmi; diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index f0fb791a7b8d..b448153df1df 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -1161,6 +1161,8 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl) rc = mdss_dsi_cmdlist_put(ctrl, &cmdreq); if (rc <= 0) { + if (!mdss_dsi_sync_wait_enable(ctrl) || + mdss_dsi_sync_wait_trigger(ctrl)) pr_err("%s: get status: fail\n", __func__); return rc; } diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 60012c71449c..695dbfa95e29 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -260,6 +260,15 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc); goto rst_gpio_err; } + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + rc = gpio_request(ctrl_pdata->avdd_en_gpio, + "avdd_enable"); + if (rc) { + pr_err("request avdd_en gpio failed, rc=%d\n", + rc); + goto avdd_en_gpio_err; + } + } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); if (rc) { @@ -272,6 +281,9 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) return rc; lcd_mode_sel_gpio_err: + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + gpio_free(ctrl_pdata->avdd_en_gpio); +avdd_en_gpio_err: gpio_free(ctrl_pdata->rst_gpio); rst_gpio_err: if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) @@ -424,6 +436,21 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) if (pdata->panel_info.rst_seq[++i]) usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); } + + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 0); + } else { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 1); + } + if (rc) { + pr_err("%s: unable to set dir for avdd_en gpio\n", + __func__); + goto exit; + } + } } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { @@ -452,6 +479,14 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) pr_debug("%s: Reset panel done\n", __func__); } } else { + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) + gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); + else + gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); + + gpio_free(ctrl_pdata->avdd_en_gpio); + } if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { gpio_set_value((ctrl_pdata->disp_en_gpio), 0); gpio_free(ctrl_pdata->disp_en_gpio); diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 40a79c4af38e..31cba148ad28 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -3607,6 +3607,16 @@ static void mdss_fb_var_to_panelinfo(struct fb_var_screeninfo *var, */ if (pinfo->is_dba_panel) pinfo->mipi.dsi_pclk_rate = pinfo->clk_rate; + + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + pinfo->lcdc.h_polarity = 0; + else + pinfo->lcdc.h_polarity = 1; + + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + pinfo->lcdc.v_polarity = 0; + else + pinfo->lcdc.v_polarity = 1; } void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 102c22cba7dd..ddc5edbe010d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -62,11 +62,6 @@ #define EDID_VENDOR_ID_SIZE 4 #define EDID_IEEE_REG_ID 0x0c03 -enum edid_sink_mode { - SINK_MODE_DVI, - SINK_MODE_HDMI -}; - enum luminance_value { NO_LUMINANCE_DATA = 3, MAXIMUM_LUMINANCE = 4, @@ -2406,7 +2401,7 @@ end: return scaninfo; } /* hdmi_edid_get_sink_scaninfo */ -static u32 hdmi_edid_get_sink_mode(void *input) +u32 hdmi_edid_get_sink_mode(void *input, u32 mode) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; bool sink_mode; @@ -2419,8 +2414,13 @@ static u32 hdmi_edid_get_sink_mode(void *input) if (edid_ctrl->edid_override && (edid_ctrl->override_data.sink_mode != -1)) sink_mode = edid_ctrl->override_data.sink_mode; - else - sink_mode = edid_ctrl->sink_mode; + else { + if (edid_ctrl->sink_mode && + (mode > 0 && mode <= HDMI_EVFRMT_END)) + sink_mode = SINK_MODE_HDMI; + else + sink_mode = SINK_MODE_DVI; + } return sink_mode; } /* hdmi_edid_get_sink_mode */ @@ -2435,10 +2435,21 @@ static u32 hdmi_edid_get_sink_mode(void *input) */ bool hdmi_edid_is_dvi_mode(void *input) { - if (hdmi_edid_get_sink_mode(input)) - return false; - else + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + int sink_mode; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); return true; + } + + if (edid_ctrl->edid_override && + (edid_ctrl->override_data.sink_mode != -1)) + sink_mode = edid_ctrl->override_data.sink_mode; + else + sink_mode = edid_ctrl->sink_mode; + + return (sink_mode == SINK_MODE_DVI); } /** diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index af802bb45f89..c604d0fbf7b2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -58,10 +58,16 @@ struct hdmi_edid_override_data { int vic; }; +enum edid_sink_mode { + SINK_MODE_DVI, + SINK_MODE_HDMI +}; + int hdmi_edid_parser(void *edid_ctrl); u32 hdmi_edid_get_raw_data(void *edid_ctrl, u8 *buf, u32 size); u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution); bool hdmi_edid_is_dvi_mode(void *input); +u32 hdmi_edid_get_sink_mode(void *edid_ctrl, u32 mode); bool hdmi_edid_sink_scramble_override(void *input); bool hdmi_edid_get_sink_scrambler_support(void *input); bool hdmi_edid_get_scdc_support(void *input); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 07592fa26a49..a9ab970fb4bc 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -66,7 +66,7 @@ #define HDMI_TX_4_MAX_PCLK_RATE 600000 #define hdmi_tx_get_fd(x) ((x && (ffs(x) > 0)) ? \ - hdmi_ctrl->feature_data[ffs(x) - 1] : 0) + hdmi_ctrl->feature_data[ffs(x) - 1] : NULL) #define hdmi_tx_set_fd(x, y) {if (x && (ffs(x) > 0)) \ hdmi_ctrl->feature_data[ffs(x) - 1] = y; } @@ -375,9 +375,12 @@ static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl) } } -static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) +static inline bool hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) { - return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)); + void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); + + return (hdmi_edid_get_sink_mode(data, + hdmi_ctrl->vic) == SINK_MODE_DVI); } /* hdmi_tx_is_dvi_mode */ static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl) @@ -2156,6 +2159,8 @@ static int hdmi_tx_init_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl) pinfo->lcdc.v_front_porch = timing.front_porch_v; pinfo->lcdc.v_pulse_width = timing.pulse_width_v; pinfo->lcdc.frame_rate = timing.refresh_rate; + pinfo->lcdc.h_polarity = timing.active_low_h; + pinfo->lcdc.v_polarity = timing.active_low_v; pinfo->type = DTV_PANEL; pinfo->pdest = DISPLAY_3; @@ -2442,6 +2447,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) struct dss_io_data *io = NULL; /* Defaults: Disable block, HDMI mode */ u32 hdmi_ctrl_reg = BIT(1); + void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -2470,7 +2476,8 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) hdmi_ctrl_reg |= BIT(2); /* Set transmission mode to DVI based in EDID info */ - if (hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID))) + if (hdmi_edid_get_sink_mode(data, + hdmi_ctrl->vic) == SINK_MODE_DVI) hdmi_ctrl_reg &= ~BIT(1); /* DVI mode */ /* @@ -2929,7 +2936,6 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, { int rc = 0; struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); - u32 is_mode_dvi; if (!hdmi_ctrl || !params) { DEV_ERR("%s: invalid input\n", __func__); @@ -2938,9 +2944,8 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, mutex_lock(&hdmi_ctrl->tx_lock); - is_mode_dvi = hdmi_tx_is_dvi_mode(hdmi_ctrl); - - if (!is_mode_dvi && hdmi_tx_is_panel_on(hdmi_ctrl)) { + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && + hdmi_tx_is_panel_on(hdmi_ctrl)) { memcpy(&hdmi_ctrl->audio_params, params, sizeof(struct msm_ext_disp_audio_setup_params)); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 102c2f994646..827013d06412 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -857,7 +857,7 @@ static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl) u32 reg_val, ndx, time_out_count, wait_time; struct hdmi_tx_ddc_data *ddc_data; int status; - int busy_wait_us; + int busy_wait_us = 0; if (!ddc_ctrl || !ddc_ctrl->io) { pr_err("invalid input\n"); @@ -1335,7 +1335,7 @@ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl) u32 time_out_count; struct hdmi_tx_ddc_data *ddc_data; u32 wait_time; - int busy_wait_us; + int busy_wait_us = 0; if (!ddc_ctrl || !ddc_ctrl->io) { pr_err("invalid input\n"); diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index d88d87bd2092..6936c4c1f3cc 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2180,7 +2180,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE); mdata->has_wb_ubwc = true; set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map); - set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map); set_bit(MDSS_CAPS_SEC_DETACH_SMMU, mdata->mdss_caps_map); mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED); break; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index cd403f19c088..5a15b557e5c7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1107,9 +1107,9 @@ static inline enum mdss_mdp_pu_type mdss_mdp_get_pu_type( if (!is_split_lm(mctl->mfd) || mdss_mdp_is_both_lm_valid(mctl)) pu_type = MDSS_MDP_DEFAULT_UPDATE; - else if (mctl->mixer_left->valid_roi) + else if (mctl->mixer_left && mctl->mixer_left->valid_roi) pu_type = MDSS_MDP_LEFT_ONLY_UPDATE; - else if (mctl->mixer_right->valid_roi) + else if (mctl->mixer_right && mctl->mixer_right->valid_roi) pu_type = MDSS_MDP_RIGHT_ONLY_UPDATE; else pr_err("%s: invalid pu_type\n", __func__); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 929eeb270f32..0da462394ab8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -571,6 +571,9 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, u64 ver_dwnscale; u64 active_line; u64 backfill_line; + struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl; + u64 pclk_rate; + struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; ver_dwnscale = (u64)src_h << PHASE_STEP_SHIFT; do_div(ver_dwnscale, dst.h); @@ -596,12 +599,26 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, total_cycle = active_line_cycle + backfill_cycle; + /* + * MDP clkrate = total_cycle * PixelClock / Dest-width + * if pixelClock not available: + * = total_cycle * fps * v_total + */ + if ((pinfo->type == MIPI_CMD_PANEL) && dst.w) { + pclk_rate = (u64)mdss_panel_get_htotal(pinfo, false) * + v_total * fps; + do_div(pclk_rate, pinfo->xres); + total_cycle *= pclk_rate; + } else { + total_cycle *= (fps * v_total); + } + pr_debug("line: active=%lld backfill=%lld vds=%lld\n", active_line, backfill_line, ver_dwnscale); pr_debug("cycle: total=%lld active=%lld backfill=%lld\n", total_cycle, active_line_cycle, backfill_cycle); - return (u32)total_cycle * (fps * v_total); + return (u32)total_cycle; } static inline bool __is_vert_downscaling(u32 src_h, @@ -659,6 +676,7 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe, if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE) rate = mdss_mdp_clk_fudge_factor(mixer, rate); + rate = min(mdata->max_mdp_clk_rate, rate); return rate; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 587150bbc9fa..d9aaac4526ea 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -58,6 +58,8 @@ struct intf_timing_params { u32 v_front_porch; u32 hsync_pulse_width; u32 vsync_pulse_width; + u32 h_polarity; + u32 v_polarity; u32 border_clr; u32 underflow_clr; @@ -641,13 +643,8 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, display_hctl = (hsync_end_x << 16) | hsync_start_x; den_polarity = 0; - if (MDSS_INTF_HDMI == ctx->intf_type) { - hsync_polarity = p->yres >= 720 ? 0 : 1; - vsync_polarity = p->yres >= 720 ? 0 : 1; - } else { - hsync_polarity = 0; - vsync_polarity = 0; - } + hsync_polarity = p->h_polarity; + vsync_polarity = p->v_polarity; polarity_ctl = (den_polarity << 2) | /* DEN Polarity */ (vsync_polarity << 1) | /* VSYNC Polarity */ (hsync_polarity << 0); /* HSYNC Polarity */ @@ -2178,7 +2175,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, itp->width = dsc->pclk_per_line; itp->xres = dsc->pclk_per_line; } - + itp->h_polarity = pinfo->lcdc.h_polarity; + itp->v_polarity = pinfo->lcdc.v_polarity; itp->h_back_porch = pinfo->lcdc.h_back_porch; itp->h_front_porch = pinfo->lcdc.h_front_porch; itp->v_back_porch = pinfo->lcdc.v_back_porch; diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 472f1e8e8e3b..a1d79a89b463 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -1812,13 +1812,16 @@ static int __validate_secure_session(struct mdss_overlay_private *mdp5_data) pr_debug("pipe count:: secure display:%d non-secure:%d secure-vid:%d,secure-cam:%d\n", sd_pipes, nonsd_pipes, secure_vid_pipes, secure_cam_pipes); + MDSS_XLOG(mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes, + secure_vid_pipes, secure_cam_pipes); if (mdss_get_sd_client_cnt() && !mdp5_data->sd_enabled) { pr_err("Secure session already enabled for other client\n"); return -EINVAL; } - if ((sd_pipes) && + if (((sd_pipes) || (mdp5_data->ctl->is_video_mode && + mdss_get_sd_client_cnt())) && (nonsd_pipes || secure_vid_pipes || secure_cam_pipes)) { pr_err("non-secure layer validation request during secure display session\n"); @@ -2410,14 +2413,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd, int layer_count = commit->input_layer_cnt; u32 ds_mode = 0; - struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe; + struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe; struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0}; struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0}; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_data_type *mdata = mfd_to_mdata(mfd); struct mdss_mdp_mixer *mixer = NULL; - struct mdp_input_layer *layer, *layer_list; + struct mdp_input_layer *layer = NULL, *layer_list; struct mdss_mdp_validate_info_t *validate_info_list = NULL; bool is_single_layer = false, force_validate; enum layer_pipe_q pipe_q_type; diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c index 22656175edf8..1ae3d0ba4ec6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_util.c +++ b/drivers/video/fbdev/msm/mdss_mdp_util.c @@ -428,8 +428,8 @@ static int mdss_mdp_get_ubwc_plane_size(struct mdss_mdp_format_params *fmt, if (fmt->format == MDP_Y_CBCR_H2V2_UBWC || fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC) { - uint32_t y_stride_alignment, uv_stride_alignment; - uint32_t y_height_alignment, uv_height_alignment; + uint32_t y_stride_alignment = 0, uv_stride_alignment = 0; + uint32_t y_height_alignment = 0, uv_height_alignment = 0; uint32_t y_tile_width = fmt_ubwc->micro.tile_width; uint32_t y_tile_height = fmt_ubwc->micro.tile_height; uint32_t uv_tile_width = y_tile_width / 2; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 37b0ca7aa44b..e8255ff45726 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -406,9 +406,10 @@ struct lcd_panel_info { /* Pad height */ u32 yres_pad; u32 frame_rate; + u32 h_polarity; + u32 v_polarity; }; - /* DSI PHY configuration */ struct mdss_dsi_phy_ctrl { char regulator[7]; /* 8996, 1 * 5 */ diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index 399a12e3dcc8..61b0518d3ee6 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -687,7 +687,7 @@ static struct mdss_rot_hw_resource *mdss_rotator_hw_alloc( struct mdss_rot_hw_resource *hw; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 pipe_ndx, offset = mdss_mdp_get_wb_ctl_support(mdata, true); - int ret; + int ret = 0; hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct mdss_rot_hw_resource), GFP_KERNEL); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 0de6290df4da..6649b6d8c437 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -37,6 +37,7 @@ * Generic range manager structs */ #include <linux/bug.h> +#include <linux/rbtree.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -48,6 +49,7 @@ enum drm_mm_search_flags { DRM_MM_SEARCH_DEFAULT = 0, DRM_MM_SEARCH_BEST = 1 << 0, DRM_MM_SEARCH_BELOW = 1 << 1, + DRM_MM_SEARCH_BOTTOM_UP = 1 << 2, }; enum drm_mm_allocator_flags { @@ -61,6 +63,8 @@ enum drm_mm_allocator_flags { struct drm_mm_node { struct list_head node_list; struct list_head hole_stack; + struct rb_node rb; + struct rb_node hole_node; unsigned hole_follows : 1; unsigned scanned_block : 1; unsigned scanned_prev_free : 1; @@ -70,6 +74,7 @@ struct drm_mm_node { unsigned long color; u64 start; u64 size; + u64 __subtree_last; struct drm_mm *mm; }; @@ -79,6 +84,10 @@ struct drm_mm { /* head_node.node_list is the list of all memory nodes, ordered * according to the (increasing) start address of the memory node. */ struct drm_mm_node head_node; + /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */ + struct rb_root interval_tree; + struct rb_root holes_tree; + unsigned int scan_check_range : 1; unsigned scan_alignment; unsigned long scan_color; @@ -301,6 +310,12 @@ void drm_mm_init(struct drm_mm *mm, void drm_mm_takedown(struct drm_mm *mm); bool drm_mm_clean(struct drm_mm *mm); +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last); + void drm_mm_init_scan(struct drm_mm *mm, u64 size, unsigned alignment, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index d9e12c1b1748..279411c42ded 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -330,7 +330,9 @@ struct mmc_devfeq_clk_scaling { atomic_t devfreq_abort; bool skip_clk_scale_freq_update; int freq_table_sz; + int pltfm_freq_table_sz; u32 *freq_table; + u32 *pltfm_freq_table; unsigned long total_busy_time_us; unsigned long target_freq; unsigned long curr_freq; diff --git a/include/linux/msm_mhi.h b/include/linux/msm_mhi.h index 01fe2e78b9d5..1704cb93e6a3 100644 --- a/include/linux/msm_mhi.h +++ b/include/linux/msm_mhi.h @@ -160,9 +160,11 @@ enum mhi_dev_ctrl { MHI_DEV_CTRL_RESUME, MHI_DEV_CTRL_POWER_OFF, MHI_DEV_CTRL_POWER_ON, + MHI_DEV_CTRL_TRIGGER_RDDM, MHI_DEV_CTRL_RDDM, MHI_DEV_CTRL_RDDM_KERNEL_PANIC, MHI_DEV_CTRL_NOTIFY_LINK_ERROR, + MHI_DEV_CTRL_MAXCMD, }; enum mhi_rddm_segment { diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h new file mode 100644 index 000000000000..035712e0993d --- /dev/null +++ b/include/media/cec-notifier.h @@ -0,0 +1,111 @@ +/* + * cec-notifier.h - notify CEC drivers of physical address changes + * + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LINUX_CEC_NOTIFIER_H +#define LINUX_CEC_NOTIFIER_H + +#include <linux/types.h> +#include <media/cec-edid.h> + +struct device; +struct edid; +struct cec_adapter; +struct cec_notifier; + +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + +/** + * cec_notifier_get - find or create a new cec_notifier for the given device. + * @dev: device that sends the events. + * + * If a notifier for device @dev already exists, then increase the refcount + * and return that notifier. + * + * If it doesn't exist, then allocate a new notifier struct and return a + * pointer to that new struct. + * + * Return NULL if the memory could not be allocated. + */ +struct cec_notifier *cec_notifier_get(struct device *dev); + +/** + * cec_notifier_put - decrease refcount and delete when the refcount reaches 0. + * @n: notifier + */ +void cec_notifier_put(struct cec_notifier *n); + +/** + * cec_notifier_set_phys_addr - set a new physical address. + * @n: the CEC notifier + * @pa: the CEC physical address + * + * Set a new CEC physical address. + */ +void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa); + +/** + * cec_notifier_set_phys_addr_from_edid - set parse the PA from the EDID. + * @n: the CEC notifier + * @edid: the struct edid pointer + * + * Parses the EDID to obtain the new CEC physical address and set it. + */ +void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid); + +/** + * cec_notifier_register - register a callback with the notifier + * @n: the CEC notifier + * @adap: the CEC adapter, passed as argument to the callback function + * @callback: the callback function + */ +void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)); + +/** + * cec_notifier_unregister - unregister the callback from the notifier. + * @n: the CEC notifier + */ +void cec_notifier_unregister(struct cec_notifier *n); + +#else +static inline struct cec_notifier *cec_notifier_get(struct device *dev) +{ + /* A non-NULL pointer is expected on success */ + return (struct cec_notifier *)0xdeadfeed; +} + +static inline void cec_notifier_put(struct cec_notifier *n) +{ +} + +static inline void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) +{ +} + +static inline void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid) +{ +} + +#endif + +#endif diff --git a/include/media/cec.h b/include/media/cec.h index 96a0aa770d61..307f5dcaf034 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -30,6 +30,7 @@ #include <linux/cec-funcs.h> #include <media/rc-core.h> #include <media/cec-edid.h> +#include <media/cec-notifier.h> /** * struct cec_devnode - cec device node @@ -173,6 +174,10 @@ struct cec_adapter { bool passthrough; struct cec_log_addrs log_addrs; +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + struct cec_notifier *notifier; +#endif + struct dentry *cec_dir; struct dentry *status_file; @@ -213,6 +218,11 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); +#ifdef CONFIG_MEDIA_CEC_NOTIFIER +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier); +#endif + #else static inline int cec_register_adapter(struct cec_adapter *adap, diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 7aa6496c7608..cc6d4fb42d9f 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -102,6 +102,13 @@ struct drm_msm_gem_new { __u32 handle; /* out */ }; +struct drm_msm_gem_svm_new { + __u64 hostptr; /* in, must be page-aligned */ + __u64 size; /* in, must be page-aligned */ + __u32 flags; /* in, mask of MSM_BO_x */ + __u32 handle; /* out */ +}; + #define MSM_INFO_IOVA 0x01 #define MSM_INFO_FLAGS (MSM_INFO_IOVA) @@ -356,6 +363,8 @@ struct drm_msm_gem_sync { #define DRM_MSM_GEM_CPU_FINI 0x05 #define DRM_MSM_GEM_SUBMIT 0x06 #define DRM_MSM_WAIT_FENCE 0x07 +/* Gap for upstream DRM_MSM_GEM_MADVISE */ +#define DRM_MSM_GEM_SVM_NEW 0x09 #define DRM_SDE_WB_CONFIG 0x40 #define DRM_MSM_REGISTER_EVENT 0x41 @@ -395,6 +404,9 @@ struct drm_msm_gem_sync { struct drm_msm_counter_read) #define DRM_IOCTL_MSM_GEM_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_GEM_SYNC,\ struct drm_msm_gem_sync) +#define DRM_IOCTL_MSM_GEM_SVM_NEW \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SVM_NEW, \ + struct drm_msm_gem_svm_new) #if defined(__cplusplus) } diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index cd6009006510..41b40f310c28 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -268,7 +268,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, struct msi_domain_ops *ops = info->ops; msi_alloc_info_t arg; struct msi_desc *desc; - int i, ret, virq; + int i, ret, virq = 0; ret = ops->msi_check(domain, info, dev); if (ret == 0) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 01a49614e942..e7c2392666cb 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -49,7 +49,6 @@ #include <linux/sched/deadline.h> #include <linux/timer.h> #include <linux/freezer.h> -#include <linux/delay.h> #include <asm/uaccess.h> @@ -1593,42 +1592,22 @@ static void init_hrtimers_cpu(int cpu) } #if defined(CONFIG_HOTPLUG_CPU) -static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, - struct hrtimer_cpu_base *new_base, - unsigned int i, - bool wait, +static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, + struct hrtimer_clock_base *new_base, bool remove_pinned) { struct hrtimer *timer; struct timerqueue_node *node; struct timerqueue_head pinned; int is_pinned; - struct hrtimer_clock_base *old_c_base = &old_base->clock_base[i]; - struct hrtimer_clock_base *new_c_base = &new_base->clock_base[i]; + bool is_hotplug = !cpu_online(old_base->cpu_base->cpu); timerqueue_init_head(&pinned); - while ((node = timerqueue_getnext(&old_c_base->active))) { + while ((node = timerqueue_getnext(&old_base->active))) { timer = container_of(node, struct hrtimer, node); - if (wait) { - /* Ensure timers are done running before continuing */ - while (hrtimer_callback_running(timer)) { - raw_spin_unlock(&old_base->lock); - raw_spin_unlock(&new_base->lock); - cpu_relax(); - /* - * cpu_relax may just be a barrier. Grant the - * run_hrtimer_list code some time to obtain the - * spinlock. - */ - udelay(2); - raw_spin_lock(&new_base->lock); - raw_spin_lock_nested(&old_base->lock, - SINGLE_DEPTH_NESTING); - } - } else { + if (is_hotplug) BUG_ON(hrtimer_callback_running(timer)); - } debug_deactivate(timer); /* @@ -1636,7 +1615,7 @@ static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, * timer could be seen as !active and just vanish away * under us on another CPU */ - __remove_hrtimer(timer, old_c_base, HRTIMER_STATE_ENQUEUED, 0); + __remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0); is_pinned = timer->state & HRTIMER_STATE_PINNED; if (!remove_pinned && is_pinned) { @@ -1644,7 +1623,7 @@ static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, continue; } - timer->base = new_c_base; + timer->base = new_base; /* * Enqueue the timers on the new cpu. This does not * reprogram the event device in case the timer @@ -1653,7 +1632,7 @@ static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, * sort out already expired timers and reprogram the * event device. */ - enqueue_hrtimer(timer, new_c_base); + enqueue_hrtimer(timer, new_base); } /* Re-queue pinned timers for non-hotplug usecase */ @@ -1661,11 +1640,11 @@ static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base, timer = container_of(node, struct hrtimer, node); timerqueue_del(&pinned, &timer->node); - enqueue_hrtimer(timer, old_c_base); + enqueue_hrtimer(timer, old_base); } } -static void __migrate_hrtimers(int scpu, bool wait, bool remove_pinned) +static void __migrate_hrtimers(int scpu, bool remove_pinned) { struct hrtimer_cpu_base *old_base, *new_base; unsigned long flags; @@ -1682,8 +1661,8 @@ static void __migrate_hrtimers(int scpu, bool wait, bool remove_pinned) raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { - migrate_hrtimer_list(old_base, new_base, i, wait, - remove_pinned); + migrate_hrtimer_list(&old_base->clock_base[i], + &new_base->clock_base[i], remove_pinned); } raw_spin_unlock(&old_base->lock); @@ -1699,12 +1678,12 @@ static void migrate_hrtimers(int scpu) BUG_ON(cpu_online(scpu)); tick_cancel_sched_timer(scpu); - __migrate_hrtimers(scpu, false, true); + __migrate_hrtimers(scpu, true); } void hrtimer_quiesce_cpu(void *cpup) { - __migrate_hrtimers(*(int *)cpup, true, false); + __migrate_hrtimers(*(int *)cpup, false); } #endif /* CONFIG_HOTPLUG_CPU */ diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 80016b329d94..051544aec37c 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -1250,7 +1250,7 @@ void run_posix_cpu_timers(struct task_struct *tsk) void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx, cputime_t *newval, cputime_t *oldval) { - unsigned long long now; + unsigned long long now = 0; WARN_ON_ONCE(clock_idx == CPUCLOCK_SCHED); cpu_timer_sample_group(clock_idx, tsk, &now); diff --git a/net/netfilter/xt_HARDIDLETIMER.c b/net/netfilter/xt_HARDIDLETIMER.c index 06322e4de632..ecb4a7fb6bf8 100644 --- a/net/netfilter/xt_HARDIDLETIMER.c +++ b/net/netfilter/xt_HARDIDLETIMER.c @@ -4,7 +4,7 @@ * Netfilter module to trigger a timer when packet matches. * After timer expires a kevent will be sent. * - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2017 The Linux Foundation. All rights reserved. * * Copyright (C) 2004, 2010 Nokia Corporation * @@ -187,6 +187,8 @@ static int hardidletimer_tg_create(struct hardidletimer_tg_info *info) pr_debug("couldn't add file to sysfs"); goto out_free_attr; } + /* notify userspace */ + kobject_uevent(hardidletimer_tg_kobj, KOBJ_ADD); list_add(&info->timer->entry, &hardidletimer_tg_list); diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 80b32de1d99c..f9eb8641dc3a 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -307,6 +307,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) pr_debug("couldn't add file to sysfs"); goto out_free_attr; } + /* notify userspace */ + kobject_uevent(idletimer_tg_kobj, KOBJ_ADD); list_add(&info->timer->entry, &idletimer_tg_list); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b2e934ff2448..1f5d18d80fba 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -991,7 +991,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr; char *sun_path = sunaddr->sun_path; int err; - unsigned int hash; + unsigned int hash = 0; struct unix_address *addr; struct hlist_head *list; struct path path = { NULL, NULL }; |
