diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml index b075341caafc1..c130733887e39 100644 --- a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml @@ -17,20 +17,16 @@ properties: const: qcom,x1e80100-camss reg: - maxItems: 17 + maxItems: 13 reg-names: items: + - const: csid_wrapper - const: csid0 - const: csid1 - const: csid2 - const: csid_lite0 - const: csid_lite1 - - const: csid_wrapper - - const: csiphy0 - - const: csiphy1 - - const: csiphy2 - - const: csiphy4 - const: csitpg0 - const: csitpg1 - const: csitpg2 @@ -40,7 +36,7 @@ properties: - const: vfe_lite1 clocks: - maxItems: 29 + maxItems: 21 clock-names: items: @@ -55,14 +51,6 @@ properties: - const: cphy_rx_clk_src - const: csid - const: csid_csiphy_rx - - const: csiphy0 - - const: csiphy0_timer - - const: csiphy1 - - const: csiphy1_timer - - const: csiphy2 - - const: csiphy2_timer - - const: csiphy4 - - const: csiphy4_timer - const: gcc_axi_hf - const: gcc_axi_sf - const: vfe0 @@ -75,7 +63,7 @@ properties: - const: vfe_lite_csid interrupts: - maxItems: 13 + maxItems: 9 interrupt-names: items: @@ -84,15 +72,17 @@ properties: - const: csid2 - const: csid_lite0 - const: csid_lite1 - - const: csiphy0 - - const: csiphy1 - - const: csiphy2 - - const: csiphy4 - const: vfe0 - const: vfe1 - const: vfe_lite0 - const: vfe_lite1 + phys: + maxItems: 4 + + phy-names: + maxItems: 4 + interconnects: maxItems: 4 @@ -118,14 +108,6 @@ properties: - const: ife1 - const: top - vdd-csiphy-0p8-supply: - description: - Phandle to a 0.8V regulator supply to a PHY. - - vdd-csiphy-1p2-supply: - description: - Phandle to 1.8V regulator supply to a PHY. - ports: $ref: /schemas/graph.yaml#/properties/ports @@ -166,13 +148,13 @@ required: - clock-names - interrupts - interrupt-names + - phys + - phy-names - interconnects - interconnect-names - iommus - power-domains - power-domain-names - - vdd-csiphy-0p8-supply - - vdd-csiphy-1p2-supply - ports additionalProperties: false @@ -190,19 +172,15 @@ examples: #address-cells = <2>; #size-cells = <2>; - camss: isp@acb7000 { + camss: isp@acb6000 { compatible = "qcom,x1e80100-camss"; - reg = <0 0x0acb7000 0 0x2000>, + reg = <0 0x0acb6000 0 0x1000>, + <0 0x0acb7000 0 0x2000>, <0 0x0acb9000 0 0x2000>, <0 0x0acbb000 0 0x2000>, <0 0x0acc6000 0 0x1000>, <0 0x0acca000 0 0x1000>, - <0 0x0acb6000 0 0x1000>, - <0 0x0ace4000 0 0x1000>, - <0 0x0ace6000 0 0x1000>, - <0 0x0ace8000 0 0x1000>, - <0 0x0acec000 0 0x4000>, <0 0x0acf6000 0 0x1000>, <0 0x0acf7000 0 0x1000>, <0 0x0acf8000 0 0x1000>, @@ -211,16 +189,12 @@ examples: <0 0x0acc7000 0 0x2000>, <0 0x0accb000 0 0x2000>; - reg-names = "csid0", + reg-names = "csid_wrapper", + "csid0", "csid1", "csid2", "csid_lite0", "csid_lite1", - "csid_wrapper", - "csiphy0", - "csiphy1", - "csiphy2", - "csiphy4", "csitpg0", "csitpg1", "csitpg2", @@ -240,14 +214,6 @@ examples: <&camcc CAM_CC_CPHY_RX_CLK_SRC>, <&camcc CAM_CC_CSID_CLK>, <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>, - <&camcc CAM_CC_CSIPHY0_CLK>, - <&camcc CAM_CC_CSI0PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY1_CLK>, - <&camcc CAM_CC_CSI1PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY2_CLK>, - <&camcc CAM_CC_CSI2PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY4_CLK>, - <&camcc CAM_CC_CSI4PHYTIMER_CLK>, <&gcc GCC_CAMERA_HF_AXI_CLK>, <&gcc GCC_CAMERA_SF_AXI_CLK>, <&camcc CAM_CC_IFE_0_CLK>, @@ -270,14 +236,6 @@ examples: "cphy_rx_clk_src", "csid", "csid_csiphy_rx", - "csiphy0", - "csiphy0_timer", - "csiphy1", - "csiphy1_timer", - "csiphy2", - "csiphy2_timer", - "csiphy4", - "csiphy4_timer", "gcc_axi_hf", "gcc_axi_sf", "vfe0", @@ -294,10 +252,6 @@ examples: , , , - , - , - , - , , , , @@ -308,15 +262,16 @@ examples: "csid2", "csid_lite0", "csid_lite1", - "csiphy0", - "csiphy1", - "csiphy2", - "csiphy4", "vfe0", "vfe1", "vfe_lite0", "vfe_lite1"; + phys = <&csiphy0>, <&csiphy1>, + <&csiphy2>, <&csiphy4>; + phy-names = "csiphy0", "csiphy1", + "csiphy2", "csiphy4"; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS @@ -348,9 +303,6 @@ examples: "ife1", "top"; - vdd-csiphy-0p8-supply = <&csiphy_0p8_supply>; - vdd-csiphy-1p2-supply = <&csiphy_1p2_supply>; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml new file mode 100644 index 0000000000000..e0976f0125164 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MIPI CSI2 Combo PHY + +maintainers: + - Bryan O'Donoghue + +description: + Qualcomm MIPI CSI2 C-PHY/D-PHY combination PHY. Connects MIPI CSI2 sensors + to Qualcomm's Camera CSI Decoder. The PHY supports both C-PHY and D-PHY + modes. + +properties: + compatible: + const: qcom,x1e80100-mipi-csi2-combo-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: camnoc_axi + - const: cpas_ahb + - const: csiphy + - const: csiphy_timer + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + vdda-0p8-supply: + description: Phandle to a 0.8V regulator supply to a PHY. + + vdda-1p2-supply: + description: Phandle to 1.2V regulator supply to a PHY. + + phy-type: + description: D-PHY or C-PHY mode + enum: [ 10, 11 ] + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - "#phy-cells" + - clocks + - clock-names + - vdda-0p8-supply + - vdda-1p2-supply + - phy-type + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + csiphy0: csiphy@ace4000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0x0ace4000 0x2000>; + #phy-cells = <0>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + + phy-type = ; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968e..4fcfb74b8d517 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21206,6 +21206,17 @@ S: Maintained F: Documentation/devicetree/bindings/media/qcom,*-iris.yaml F: drivers/media/platform/qcom/iris/ +QUALCOMM MIPI CSI2 PHY DRIVER +M: Bryan O'Donoghue +L: linux-phy@lists.infradead.org +L: linux-media@vger.kernel.org +L: linux-arm-msm@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.c +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.h +F: include/dt-bindings/phy/phy-qcom-mipi-csi2* + QUALCOMM NAND CONTROLLER DRIVER M: Manivannan Sadhasivam L: linux-mtd@lists.infradead.org diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 7bbd639a9ddfa..459cca36493e2 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -1037,7 +1037,7 @@ static int imx412_power_on(struct device *dev) goto error_reset; } - usleep_range(1000, 1200); + usleep_range(10000, 12000); return 0; diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/platform/qcom/camss/Kconfig index 4eda48cb1adf0..1edc5e5a1829e 100644 --- a/drivers/media/platform/qcom/camss/Kconfig +++ b/drivers/media/platform/qcom/camss/Kconfig @@ -7,3 +7,4 @@ config VIDEO_QCOM_CAMSS select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_SG select V4L2_FWNODE + select PHY_QCOM_MIPI_CSI2 diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 2de97f58f9ae4..185a51aa73d10 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -131,10 +132,10 @@ static u8 csiphy_get_bpp(const struct csiphy_format_info *formats, } /* - * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module + * csiphy_set_clock_rates_legacy - Calculate and set clock rates on CSIPHY module * @csiphy: CSIPHY device */ -static int csiphy_set_clock_rates(struct csiphy_device *csiphy) +static int csiphy_set_clock_rates_legacy(struct csiphy_device *csiphy) { struct device *dev = csiphy->camss->dev; s64 link_freq; @@ -200,7 +201,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) * * Return 0 on success or a negative error code otherwise */ -static int csiphy_set_power(struct v4l2_subdev *sd, int on) +static int csiphy_set_power_legacy(struct v4l2_subdev *sd, int on) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); struct device *dev = csiphy->camss->dev; @@ -219,7 +220,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) return ret; } - ret = csiphy_set_clock_rates(csiphy); + ret = csiphy_set_clock_rates_legacy(csiphy); if (ret < 0) { regulator_bulk_disable(csiphy->num_supplies, csiphy->supplies); @@ -254,7 +255,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) } /* - * csiphy_stream_on - Enable streaming on CSIPHY module + * csiphy_stream_on_legacy - Enable streaming on CSIPHY module * @csiphy: CSIPHY device * * Helper function to enable streaming on CSIPHY module. @@ -262,7 +263,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) * * Return 0 on success or a negative error code otherwise */ -static int csiphy_stream_on(struct csiphy_device *csiphy) +static int csiphy_stream_on_legacy(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; @@ -300,6 +301,63 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) return 0; } +/* + * eam_off - Disable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to disable streaming on CSIPHY module + */ +static void csiphy_stream_off_legacy(struct csiphy_device *csiphy) +{ + csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); +} + +/* + * csiphy_stream_on - Enable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to enable streaming on CSIPHY module. + * Main configuration of CSIPHY module is also done here. + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_stream_on(struct csiphy_device *csiphy) +{ + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; + struct phy_configure_opts_mipi_dphy *dphy_cfg; + union phy_configure_opts dphy_opts = { 0 }; + struct device *dev = csiphy->camss->dev; + s64 link_freq; + int ret; + + dphy_cfg = &dphy_opts.mipi_dphy; + + link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); + + if (link_freq < 0) { + dev_err(dev, + "Cannot get CSI2 transmitter's link frequency\n"); + return -EINVAL; + } + + phy_mipi_dphy_get_default_config_for_hsclk(link_freq, num_lanes, dphy_cfg); + + phy_set_mode(csiphy->phy, PHY_MODE_MIPI_DPHY); + + ret = phy_configure(csiphy->phy, &dphy_opts); + if (ret) { + dev_err(dev, "failed to configure MIPI D-PHY\n"); + goto error; + } + + return phy_power_on(csiphy->phy); + +error: + return ret; +} + /* * csiphy_stream_off - Disable streaming on CSIPHY module * @csiphy: CSIPHY device @@ -308,9 +366,28 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); + phy_power_off(csiphy->phy); } +/* + * csiphy_set_stream - Enable/disable streaming on CSIPHY module + * @sd: CSIPHY V4L2 subdevice + * @enable: Requested streaming state + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_set_stream_legacy(struct v4l2_subdev *sd, int enable) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + int ret = 0; + + if (enable) + ret = csiphy_stream_on_legacy(csiphy); + else + csiphy_stream_off_legacy(csiphy); + + return ret; +} /* * csiphy_set_stream - Enable/disable streaming on CSIPHY module @@ -568,16 +645,16 @@ static bool csiphy_match_clock_name(const char *clock_name, const char *format, } /* - * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * msm_csiphy_subdev_init_legacy - Initialize CSIPHY device structure and resources * @csiphy: CSIPHY device * @res: CSIPHY module resources table * @id: CSIPHY module id * * Return 0 on success or a negative error code otherwise */ -int msm_csiphy_subdev_init(struct camss *camss, - struct csiphy_device *csiphy, - const struct camss_subdev_resources *res, u8 id) +int msm_csiphy_subdev_init_legacy(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id) { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); @@ -715,6 +792,43 @@ int msm_csiphy_subdev_init(struct camss *camss, return ret; } +/* + * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * @csiphy: CSIPHY device + * @res: CSIPHY module resources table + * @id: CSIPHY module id + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id) +{ + struct device *dev = camss->dev; + int ret; + + csiphy->camss = camss; + csiphy->id = id; + csiphy->cfg.combo_mode = 0; + csiphy->res = &res->csiphy; + + snprintf(csiphy->name, ARRAY_SIZE(csiphy->name), "csiphy%d", + csiphy->id); + + csiphy->phy = devm_phy_get(dev, csiphy->name); + + if (IS_ERR(csiphy->phy)) { + dev_err(dev, "failed to get phy %s %d\n", csiphy->name, ret); + return PTR_ERR(csiphy->phy); + } + + ret = phy_init(csiphy->phy); + if (ret) + dev_err(dev, "phy %s init fail %d\n", csiphy->name, ret); + + return ret; +} + /* * csiphy_link_setup - Setup CSIPHY connections * @entity: Pointer to media entity structure @@ -749,8 +863,12 @@ static int csiphy_link_setup(struct media_entity *entity, return 0; } -static const struct v4l2_subdev_core_ops csiphy_core_ops = { - .s_power = csiphy_set_power, +static const struct v4l2_subdev_core_ops csiphy_core_ops_legacy = { + .s_power = csiphy_set_power_legacy, +}; + +static const struct v4l2_subdev_video_ops csiphy_video_ops_legacy = { + .s_stream = csiphy_set_stream_legacy, }; static const struct v4l2_subdev_video_ops csiphy_video_ops = { @@ -764,8 +882,13 @@ static const struct v4l2_subdev_pad_ops csiphy_pad_ops = { .set_fmt = csiphy_set_format, }; +static const struct v4l2_subdev_ops csiphy_v4l2_ops_legacy = { + .core = &csiphy_core_ops_legacy, + .video = &csiphy_video_ops_legacy, + .pad = &csiphy_pad_ops, +}; + static const struct v4l2_subdev_ops csiphy_v4l2_ops = { - .core = &csiphy_core_ops, .video = &csiphy_video_ops, .pad = &csiphy_pad_ops, }; @@ -794,7 +917,11 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, struct device *dev = csiphy->camss->dev; int ret; - v4l2_subdev_init(sd, &csiphy_v4l2_ops); + if (IS_ERR(csiphy->phy)) + v4l2_subdev_init(sd, &csiphy_v4l2_ops_legacy); + else + v4l2_subdev_init(sd, &csiphy_v4l2_ops); + sd->internal_ops = &csiphy_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 895f80003c441..48398c331fe14 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -94,6 +95,7 @@ struct csiphy_device_regs { struct csiphy_device { struct camss *camss; + struct phy *phy; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSIPHY_PADS_NUM]; @@ -101,6 +103,7 @@ struct csiphy_device { void __iomem *base_clk_mux; u32 irq; char irq_name[30]; + char name[16]; struct camss_clock *clock; bool *rate_set; int nclocks; @@ -115,6 +118,10 @@ struct csiphy_device { struct camss_subdev_resources; +int msm_csiphy_subdev_init_legacy(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id); + int msm_csiphy_subdev_init(struct camss *camss, struct csiphy_device *csiphy, const struct camss_subdev_resources *res, u8 id); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 2fbcd0e343aac..12d9ada07ee9c 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3081,13 +3081,6 @@ static const struct resources_icc icc_res_sa8775p[] = { static const struct camss_subdev_resources csiphy_res_x1e80100[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy0", "csiphy0_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy0" }, - .interrupt = { "csiphy0" }, .csiphy = { .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3096,13 +3089,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY1 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy1", "csiphy1_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy1" }, - .interrupt = { "csiphy1" }, .csiphy = { .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3111,13 +3097,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY2 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy2", "csiphy2_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy2" }, - .interrupt = { "csiphy2" }, .csiphy = { .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3126,13 +3105,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY4 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy4", "csiphy4_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy4" }, - .interrupt = { "csiphy4" }, .csiphy = { .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3676,18 +3648,40 @@ static int camss_init_subdevices(struct camss *camss) { struct platform_device *pdev = to_platform_device(camss->dev); const struct camss_resources *res = camss->res; + struct device_node *phy_np; unsigned int i; int ret; for (i = 0; i < camss->res->csiphy_num; i++) { - ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], - &res->csiphy_res[i], - res->csiphy_res[i].csiphy.id); - if (ret < 0) { - dev_err(camss->dev, - "Failed to init csiphy%d sub-device: %d\n", - i, ret); - return ret; + phy_np = of_parse_phandle(pdev->dev.of_node, "phys", i); + if (phy_np && of_device_is_available(phy_np)) { + ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], + &res->csiphy_res[i], + res->csiphy_res[i].csiphy.id); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + } + } + + if (!phy_np) { + if (!res->legacy_phy) + return -ENODEV; + + for (i = 0; i < camss->res->csiphy_num; i++) { + ret = msm_csiphy_subdev_init_legacy(camss, &camss->csiphy[i], + &res->csiphy_res[i], + res->csiphy_res[i].csiphy.id); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + camss->csiphy[i].phy = ERR_PTR(-ENODEV); } } @@ -3764,6 +3758,9 @@ static int camss_link_entities(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) { for (j = 0; j < camss->res->csid_num; j++) { + if (!camss->csiphy[i].phy) + continue; + ret = media_create_pad_link(&camss->csiphy[i].subdev.entity, MSM_CSIPHY_PAD_SRC, &camss->csid[j].subdev.entity, @@ -3873,6 +3870,9 @@ static int camss_register_entities(struct camss *camss) int ret; for (i = 0; i < camss->res->csiphy_num; i++) { + if (!camss->csiphy[i].phy) + continue; + ret = msm_csiphy_register_entity(&camss->csiphy[i], &camss->v4l2_dev); if (ret < 0) { @@ -3928,8 +3928,10 @@ static int camss_register_entities(struct camss *camss) i = camss->res->csiphy_num; err_reg_csiphy: - for (i--; i >= 0; i--) - msm_csiphy_unregister_entity(&camss->csiphy[i]); + for (i--; i >= 0; i--) { + if (camss->csiphy[i].phy) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + } return ret; } @@ -3944,8 +3946,10 @@ static void camss_unregister_entities(struct camss *camss) { unsigned int i; - for (i = 0; i < camss->res->csiphy_num; i++) - msm_csiphy_unregister_entity(&camss->csiphy[i]); + for (i = 0; i < camss->res->csiphy_num; i++) { + if (camss->csiphy[i].phy) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + } for (i = 0; i < camss->res->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); @@ -4293,6 +4297,7 @@ static void camss_remove(struct platform_device *pdev) static const struct camss_resources msm8916_resources = { .version = CAMSS_8x16, + .legacy_phy = true, .csiphy_res = csiphy_res_8x16, .csid_res = csid_res_8x16, .ispif_res = &ispif_res_8x16, @@ -4304,6 +4309,7 @@ static const struct camss_resources msm8916_resources = { static const struct camss_resources msm8953_resources = { .version = CAMSS_8x53, + .legacy_phy = true, .icc_res = icc_res_8x53, .icc_path_num = ARRAY_SIZE(icc_res_8x53), .csiphy_res = csiphy_res_8x96, @@ -4317,6 +4323,7 @@ static const struct camss_resources msm8953_resources = { static const struct camss_resources msm8996_resources = { .version = CAMSS_8x96, + .legacy_phy = true, .csiphy_res = csiphy_res_8x96, .csid_res = csid_res_8x96, .ispif_res = &ispif_res_8x96, @@ -4368,6 +4375,7 @@ static const struct camss_resources sa8775p_resources = { static const struct camss_resources sdm660_resources = { .version = CAMSS_660, + .legacy_phy = true, .csiphy_res = csiphy_res_660, .csid_res = csid_res_660, .ispif_res = &ispif_res_660, @@ -4379,6 +4387,7 @@ static const struct camss_resources sdm660_resources = { static const struct camss_resources sdm670_resources = { .version = CAMSS_845, + .legacy_phy = true, .csiphy_res = csiphy_res_670, .csid_res = csid_res_670, .vfe_res = vfe_res_670, @@ -4390,6 +4399,7 @@ static const struct camss_resources sdm670_resources = { static const struct camss_resources sdm845_resources = { .version = CAMSS_845, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_845, .csid_res = csid_res_845, .vfe_res = vfe_res_845, @@ -4401,6 +4411,7 @@ static const struct camss_resources sdm845_resources = { static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_8250, .csid_res = csid_res_8250, .vfe_res = vfe_res_8250, @@ -4414,6 +4425,7 @@ static const struct camss_resources sm8250_resources = { static const struct camss_resources sc8280xp_resources = { .version = CAMSS_8280XP, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_sc8280xp, .csid_res = csid_res_sc8280xp, .ispif_res = NULL, @@ -4428,6 +4440,7 @@ static const struct camss_resources sc8280xp_resources = { static const struct camss_resources sc7280_resources = { .version = CAMSS_7280, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_7280, .csid_res = csid_res_7280, .vfe_res = vfe_res_7280, @@ -4441,6 +4454,7 @@ static const struct camss_resources sc7280_resources = { static const struct camss_resources sm8550_resources = { .version = CAMSS_8550, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_8550, .csid_res = csid_res_8550, .vfe_res = vfe_res_8550, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index a70fbc78ccc30..3ca10f79f0f27 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -100,6 +100,7 @@ enum icc_count { struct camss_resources { enum camss_version version; const char *pd_name; + const bool legacy_phy; const struct camss_subdev_resources *csiphy_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 60a0ead127fa9..24263f8f30a92 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -28,6 +28,17 @@ config PHY_QCOM_EDP Enable this driver to support the Qualcomm eDP PHY found in various Qualcomm chipsets. +config PHY_QCOM_MIPI_CSI2 + tristate "Qualcomm MIPI CSI2 PHY driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on OF + depends on COMMON_CLK + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Enable this to support the MIPI CSI2 PHY driver found in various + Qualcomm chipsets. + config PHY_QCOM_IPQ4019_USB tristate "Qualcomm IPQ4019 USB PHY driver" depends on OF && (ARCH_QCOM || COMPILE_TEST) diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index b71a6a0bed3f1..12f15d1387260 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -6,6 +6,11 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o obj-$(CONFIG_PHY_QCOM_M31_EUSB) += phy-qcom-m31-eusb2.o + +phy-qcom-mipi-csi2-objs += phy-qcom-mipi-csi2-core.o \ + phy-qcom-mipi-csi2-3ph-dphy.o +obj-$(CONFIG_PHY_QCOM_MIPI_CSI2) += phy-qcom-mipi-csi2.o + obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o phy-qcom-qmp-usbc.o diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c new file mode 100644 index 0000000000000..1a99efee88cc9 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-phy_qcom_mipi_csi2-3ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2025 Linaro Ltd. + */ +#define DEBUG +#include +#include +#include + +#include "phy-qcom-mipi-csi2.h" + +#define CSIPHY_3PH_LNn_CFG1(n) (0x000 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG (BIT(7) | BIT(6)) +#define CSIPHY_3PH_LNn_CFG2(n) (0x004 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT BIT(3) +#define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4 +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660 0xa5 +#define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02 +#define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50 +#define CSIPHY_3PH_LNn_TEST_IMP(n) (0x01c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP 0xa +#define CSIPHY_3PH_LNn_MISC1(n) (0x028 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_MISC1_IS_CLKLANE BIT(2) +#define CSIPHY_3PH_LNn_CFG6(n) (0x02c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT BIT(0) +#define CSIPHY_3PH_LNn_CFG7(n) (0x030 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG7_SWI_T_INIT 0x2 +#define CSIPHY_3PH_LNn_CFG8(n) (0x034 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP BIT(0) +#define CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE BIT(1) +#define CSIPHY_3PH_LNn_CFG9(n) (0x038 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP 0x1 +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8 + +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n)) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) +#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n)) + +#define CSIPHY_DEFAULT_PARAMS 0 +#define CSIPHY_LANE_ENABLE 1 +#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2 +#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3 +#define CSIPHY_DNP_PARAMS 4 +#define CSIPHY_2PH_REGS 5 +#define CSIPHY_3PH_REGS 6 +#define CSIPHY_SKEW_CAL 7 + +/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */ +static const struct +mipi_csi2phy_lane_regs lane_regs_x1e80100[] = { + /* Power up lanes 2ph mode */ + {0x1014, 0xD5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x101C, 0x7A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x1018, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x005C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0064, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0E94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0EA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x045C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0464, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x085C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0860, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0864, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x0C5C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0C60, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0C64, 0x7F, 0x00, CSIPHY_SKEW_CAL}, +}; + +static inline const struct mipi_csi2phy_device_regs * +csi2phy_dev_to_regs(const struct mipi_csi2phy_device *csi2phy) +{ + return &csi2phy->soc_cfg->reg_info; +} + +static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + u32 hw_version; + + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); + + hw_version = readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12)); + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8; + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16; + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24; + + csi2phy->hw_version = hw_version; + + dev_dbg(csi2phy->dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); +} + +/* + * phy_qcom_mipi_csi2_reset - Perform software reset on CSIPHY module + * @phy_qcom_mipi_csi2: CSIPHY device + */ +static void phy_qcom_mipi_csi2_reset(struct mipi_csi2phy_device *csi2phy) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + + writel_relaxed(0x1, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); + usleep_range(5000, 8000); + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); +} + +static irqreturn_t phy_qcom_mipi_csi2_isr(int irq, void *dev) +{ + const struct mipi_csi2phy_device *csi2phy = dev; + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + int i; + + for (i = 0; i < 11; i++) { + int c = i + 22; + u8 val = readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i)); + + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c)); + } + + writel_relaxed(0x1, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10)); + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10)); + + for (i = 22; i < 33; i++) { + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i)); + } + + return IRQ_HANDLED; +} + +/* + * phy_qcom_mipi_csi2_settle_cnt_calc - Calculate settle count value + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter link frequency. + * + * Return settle count value or 0 if the CSI2 link frequency + * is not available + */ +static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate) +{ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + if (link_freq <= 0) + return 0; + + ui = div_u64(1000000000000LL, link_freq); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_settle = t_hs_prepare_max; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 6; + + return settle_cnt; +} + +static void phy_qcom_mipi_csi2_gen1_config_lanes(struct mipi_csi2phy_device *csi2phy, + struct mipi_csi2phy_stream_cfg *cfg, + u8 settle_cnt) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg; + int i, l = 0; + u8 val; + + for (i = 0; i <= cfg->num_data_lanes; i++) { + if (i == cfg->num_data_lanes) + l = 7; + else + l = lane_cfg->data[i].pos * 2; + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + val |= 0x17; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG2(l)); + + val = settle_cnt; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG3(l)); + + val = CSIPHY_3PH_LNn_CFG5_T_HS_DTERM | + CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG5(l)); + + val = CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG6(l)); + + val = CSIPHY_3PH_LNn_CFG7_SWI_T_INIT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG7(l)); + + val = CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP | + CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG8(l)); + + val = CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG9(l)); + + val = CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_TEST_IMP(l)); + + val = CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_LNn_CSI_LANE_CTRL15(l)); + } + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG1(l)); + + if (regs->generation == GEN1_660) + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660; + else + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG4(l)); + + val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_MISC1(l)); +} + +static void +phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy, + u8 settle_cnt) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + const struct mipi_csi2phy_lane_regs *r = regs->init_seq; + int i, array_size = regs->lane_array_size; + u32 val; + + for (i = 0; i < array_size; i++, r++) { + switch (r->mipi_csi2phy_param_type) { + case CSIPHY_SETTLE_CNT_LOWER_BYTE: + val = settle_cnt & 0xff; + break; + case CSIPHY_SKEW_CAL: + /* TODO: support application of skew from dt flag */ + continue; + case CSIPHY_DNP_PARAMS: + continue; + default: + val = r->reg_data; + break; + } + writel_relaxed(val, csi2phy->base + r->reg_addr); + if (r->delay_us) + udelay(r->delay_us); + } +} + +static bool phy_qcom_mipi_csi2_is_gen2(struct mipi_csi2phy_device *csi2phy) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + + return regs->generation == GEN2; +} + +static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy, + struct mipi_csi2phy_stream_cfg *cfg) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg; + u8 settle_cnt; + u8 val; + int i; + + settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE; + for (i = 0; i < cfg->num_data_lanes; i++) + val |= BIT(lane_cfg->data[i].pos * 2); + + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5)); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); + + val = 0x02; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 7)); + + val = 0x00; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); + + if (phy_qcom_mipi_csi2_is_gen2(csi2phy)) + phy_qcom_mipi_csi2_gen2_config_lanes(csi2phy, settle_cnt); + else + phy_qcom_mipi_csi2_gen1_config_lanes(csi2phy, cfg, settle_cnt); + + /* IRQ_MASK registers - disable all interrupts */ + for (i = 11; i < 22; i++) { + writel_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i)); + } + + return 0; +} + +static void +phy_qcom_mipi_csi2_lanes_disable(struct mipi_csi2phy_device *csi2phy, + struct mipi_csi2phy_stream_cfg *cfg) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + + writel_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5)); + + writel_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); +} + +static int phy_qcom_mipi_csi2_init(struct mipi_csi2phy_device *csi2phy) +{ + return 0; +} + +const struct mipi_csi2phy_hw_ops phy_qcom_mipi_csi2_ops_3ph_1_0 = { + .hw_version_read = phy_qcom_mipi_csi2_hw_version_read, + .reset = phy_qcom_mipi_csi2_reset, + .lanes_enable = phy_qcom_mipi_csi2_lanes_enable, + .lanes_disable = phy_qcom_mipi_csi2_lanes_disable, + .isr = phy_qcom_mipi_csi2_isr, + .init = phy_qcom_mipi_csi2_init, +}; + +const struct mipi_csi2phy_clk_freq zero = { 0 }; + +const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy = { + .freq = { + 300000000, 400000000, 480000000 + }, + .num_freq = 3, +}; + +const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy_timer = { + .freq = { + 266666667, 400000000 + }, + .num_freq = 2, +}; + +const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e = { + .ops = &phy_qcom_mipi_csi2_ops_3ph_1_0, + .reg_info = { + .init_seq = lane_regs_x1e80100, + .lane_array_size = ARRAY_SIZE(lane_regs_x1e80100), + .offset = 0x1000, + .generation = GEN2, + }, + .supply_names = (const char *[]){ + "vdda-0p8", + "vdda-1p2" + }, + .num_supplies = 2, + .clk_names = (const char *[]) { + "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer" + }, + .num_clk = 4, + .clk_freq = { + zero, + zero, + dphy_4nm_x1e_csiphy, + dphy_4nm_x1e_csiphy_timer, + }, +}; diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c new file mode 100644 index 0000000000000..1def2d1258d9d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-mipi-csi2.h" + +#define CAMSS_CLOCK_MARGIN_NUMERATOR 105 +#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 + +static inline void phy_qcom_mipi_csi2_add_clock_margin(u64 *rate) +{ + *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; + *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); +} + +static int +phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy, + s64 link_freq) +{ + const struct mipi_csi2phy_soc_cfg *soc_cfg = csi2phy->soc_cfg; + struct device *dev = csi2phy->dev; + int i, j; + int ret; + + for (i = 0; i < soc_cfg->num_clk; i++) { + const struct mipi_csi2phy_clk_freq *clk_freq = &soc_cfg->clk_freq[i]; + const char *clk_name = soc_cfg->clk_names[i]; + struct clk *clk = csi2phy->clks[i].clk; + u64 min_rate = link_freq / 4; + long round_rate; + + phy_qcom_mipi_csi2_add_clock_margin(&min_rate); + + /* This clock should be enabled only not set */ + if (!clk_freq->num_freq) + continue; + + for (j = 0; j < clk_freq->num_freq; j++) + if (min_rate < clk_freq->freq[j]) + break; + + if (j == clk_freq->num_freq) { + dev_err(dev, + "Pixel clock %llu is too high for %s\n", + min_rate, clk_name); + return -EINVAL; + } + + /* if sensor pixel clock is not available + * set highest possible CSIPHY clock rate + */ + if (min_rate == 0) + j = clk_freq->num_freq - 1; + + round_rate = clk_round_rate(clk, clk_freq->freq[j]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + csi2phy->timer_clk_rate = round_rate; + + dev_dbg(dev, "set clk %s %lu Hz\n", + clk_name, round_rate); + + ret = clk_set_rate(clk, csi2phy->timer_clk_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int phy_qcom_mipi_csi2_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy); + struct phy_configure_opts_mipi_dphy *dphy_cfg_opts = &opts->mipi_dphy; + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg; + int ret; + int i; + + ret = phy_mipi_dphy_config_validate(dphy_cfg_opts); + if (ret) + return ret; + + if (dphy_cfg_opts->lanes < 1 || dphy_cfg_opts->lanes > CSI2_MAX_DATA_LANES) + return -EINVAL; + + stream_cfg->combo_mode = 0; + stream_cfg->link_freq = dphy_cfg_opts->hs_clk_rate; + stream_cfg->num_data_lanes = dphy_cfg_opts->lanes; + + /* + * phy_configure_opts_mipi_dphy.lanes starts from zero to + * the maximum number of enabled lanes. + * + * TODO: add support for bitmask of enabled lanes and polarities + * of those lanes to the phy_configure_opts_mipi_dphy struct. + * For now take the polarities as zero and the position as fixed + * this is fine as no current upstream implementation maps otherwise. + */ + for (i = 0; i < stream_cfg->num_data_lanes; i++) { + stream_cfg->lane_cfg.data[i].pol = 0; + stream_cfg->lane_cfg.data[i].pos = i; + } + + stream_cfg->lane_cfg.clk.pol = 0; + stream_cfg->lane_cfg.clk.pos = 7; + + return 0; +} + +static int phy_qcom_mipi_csi2_power_on(struct phy *phy) +{ + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy); + const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops; + struct device *dev = &phy->dev; + int ret; + + ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies, + csi2phy->supplies); + if (ret) + return ret; + + ret = phy_qcom_mipi_csi2_set_clock_rates(csi2phy, csi2phy->stream_cfg.link_freq); + if (ret) + goto poweroff_phy; + + ret = clk_bulk_prepare_enable(csi2phy->soc_cfg->num_clk, + csi2phy->clks); + if (ret) { + dev_err(dev, "failed to enable clocks, %d\n", ret); + goto poweroff_phy; + } + + ops->hw_version_read(csi2phy); + + return ops->lanes_enable(csi2phy, &csi2phy->stream_cfg); + +poweroff_phy: + regulator_bulk_disable(csi2phy->soc_cfg->num_supplies, + csi2phy->supplies); + + return ret; +} + +static int phy_qcom_mipi_csi2_power_off(struct phy *phy) +{ + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk, + csi2phy->clks); + regulator_bulk_disable(csi2phy->soc_cfg->num_supplies, + csi2phy->supplies); + + return 0; +} + +static const struct phy_ops phy_qcom_mipi_csi2_ops = { + .configure = phy_qcom_mipi_csi2_configure, + .power_on = phy_qcom_mipi_csi2_power_on, + .power_off = phy_qcom_mipi_csi2_power_off, + .owner = THIS_MODULE, +}; + +static int phy_qcom_mipi_csi2_probe(struct platform_device *pdev) +{ + unsigned int i, num_clk, num_supplies; + struct mipi_csi2phy_device *csi2phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct phy *generic_phy; + int ret; + + csi2phy = devm_kzalloc(dev, sizeof(*csi2phy), GFP_KERNEL); + if (!csi2phy) + return -ENOMEM; + + csi2phy->dev = dev; + csi2phy->soc_cfg = device_get_match_data(&pdev->dev); + + if (!csi2phy->soc_cfg) + return -EINVAL; + + num_clk = csi2phy->soc_cfg->num_clk; + csi2phy->clks = devm_kzalloc(dev, sizeof(*csi2phy->clks) * num_clk, GFP_KERNEL); + if (!csi2phy->clks) + return -ENOMEM; + + for (i = 0; i < num_clk; i++) + csi2phy->clks[i].id = csi2phy->soc_cfg->clk_names[i]; + + ret = devm_clk_bulk_get(dev, num_clk, csi2phy->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(num_clk, csi2phy->clks); + if (ret) { + dev_err(dev, "apq8016 clk_enable failed\n"); + return ret; + } + + num_supplies = csi2phy->soc_cfg->num_supplies; + csi2phy->supplies = devm_kzalloc(dev, sizeof(*csi2phy->supplies) * num_supplies, + GFP_KERNEL); + if (!csi2phy->supplies) + return -ENOMEM; + + for (i = 0; i < num_supplies; i++) + csi2phy->supplies[i].supply = csi2phy->soc_cfg->supply_names[i]; + + ret = devm_regulator_bulk_get(dev, num_supplies, csi2phy->supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + csi2phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi2phy->base)) + return PTR_ERR(csi2phy->base); + + generic_phy = devm_phy_create(dev, NULL, &phy_qcom_mipi_csi2_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create phy, %d\n", ret); + return ret; + } + csi2phy->phy = generic_phy; + + phy_set_drvdata(generic_phy, csi2phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_dbg(dev, "Registered MIPI CSI2 PHY device\n"); + else + pm_runtime_disable(dev); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_qcom_mipi_csi2_of_match_table[] = { + { .compatible = "qcom,x1e80100-mipi-csi2-combo-phy", .data = &mipi_csi2_dphy_4nm_x1e }, + { } +}; +MODULE_DEVICE_TABLE(of, phy_qcom_mipi_csi2_of_match_table); + +static struct platform_driver phy_qcom_mipi_csi2_driver = { + .probe = phy_qcom_mipi_csi2_probe, + .driver = { + .name = "qcom-mipi-csi2-phy", + .of_match_table = phy_qcom_mipi_csi2_of_match_table, + }, +}; + +module_platform_driver(phy_qcom_mipi_csi2_driver); + +MODULE_DESCRIPTION("Qualcomm MIPI CSI2 PHY driver"); +MODULE_DESCRIPTION("Bryan O'Donoghue "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h new file mode 100644 index 0000000000000..adcb371c9bf6f --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * Qualcomm MIPI CSI2 CPHY/DPHY driver + * + * Copyright (C) 2025 Linaro Ltd. + */ +#ifndef __PHY_QCOM_MIPI_CSI2_H__ +#define __PHY_QCOM_MIPI_CSI2_H__ + +#include + +#define CSI2_MAX_DATA_LANES 4 + +struct mipi_csi2phy_lane { + u8 pos; + u8 pol; +}; + +struct mipi_csi2phy_lanes_cfg { + struct mipi_csi2phy_lane data[CSI2_MAX_DATA_LANES]; + struct mipi_csi2phy_lane clk; +}; + +struct mipi_csi2phy_stream_cfg { + u8 combo_mode; + s64 link_freq; + u8 num_data_lanes; + struct mipi_csi2phy_lanes_cfg lane_cfg; +}; + +struct mipi_csi2phy_device; + +struct mipi_csi2phy_hw_ops { + void (*hw_version_read)(struct mipi_csi2phy_device *csi2phy_dev); + void (*reset)(struct mipi_csi2phy_device *csi2phy_dev); + int (*lanes_enable)(struct mipi_csi2phy_device *csi2phy_dev, + struct mipi_csi2phy_stream_cfg *cfg); + void (*lanes_disable)(struct mipi_csi2phy_device *csi2phy_dev, + struct mipi_csi2phy_stream_cfg *cfg); + irqreturn_t (*isr)(int irq, void *dev); + int (*init)(struct mipi_csi2phy_device *csi2phy_dev); +}; + +struct mipi_csi2phy_lane_regs { + const s32 reg_addr; + const s32 reg_data; + const u32 delay_us; + const u32 mipi_csi2phy_param_type; +}; + +struct mipi_csi2phy_device_regs { + const struct mipi_csi2phy_lane_regs *init_seq; + const int lane_array_size; + const u32 offset; + enum { + GEN1 = 0, + GEN1_660, + GEN1_670, + GEN2, + } generation; +}; + +#define MAX_CSI2PHY_CLKS 8 +struct mipi_csi2phy_clk_freq { + u32 num_freq; + u32 freq[MAX_CSI2PHY_CLKS]; +}; + +struct mipi_csi2phy_soc_cfg { + const struct mipi_csi2phy_hw_ops *ops; + const struct mipi_csi2phy_device_regs reg_info; + + const char ** const supply_names; + const unsigned int num_supplies; + + const char ** const clk_names; + const unsigned int num_clk; + + const struct mipi_csi2phy_clk_freq clk_freq[]; +}; + +struct mipi_csi2phy_device { + struct device *dev; + + struct phy *phy; + void __iomem *base; + + struct clk_bulk_data *clks; + struct regulator_bulk_data *supplies; + u32 timer_clk_rate; + + const struct mipi_csi2phy_soc_cfg *soc_cfg; + struct mipi_csi2phy_stream_cfg stream_cfg; + + u32 hw_version; +}; + +extern const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e; + +#endif /* __PHY_QCOM_MIPI_CSI2_H__ */