1 // SPDX-License-Identifier: GPL-2.0
3 * MediaTek MIPI CSI v0.5 driver
5 * Copyright (c) 2023, MediaTek Inc.
6 * Copyright (c) 2023, BayLibre Inc.
9 #include <dt-bindings/phy/phy.h>
10 #include <linux/bitfield.h>
11 #include <linux/delay.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
19 #include "phy-mtk-io.h"
20 #include "phy-mtk-mipi-csi-0-5-rx-reg.h"
22 #define CSIXB_OFFSET 0x1000
24 struct mtk_mipi_cdphy_port
{
39 static void mtk_phy_csi_cdphy_ana_eq_tune(void __iomem
*base
)
41 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI0A_L0_T0AB_EQ_IS
, 1);
42 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI0A_L0_T0AB_EQ_BW
, 1);
43 mtk_phy_update_field(base
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI0A_L1_T1AB_EQ_IS
, 1);
44 mtk_phy_update_field(base
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI0A_L1_T1AB_EQ_BW
, 1);
45 mtk_phy_update_field(base
+ MIPI_RX_ANA20_CSI0A
, RG_CSI0A_L2_T1BC_EQ_IS
, 1);
46 mtk_phy_update_field(base
+ MIPI_RX_ANA20_CSI0A
, RG_CSI0A_L2_T1BC_EQ_BW
, 1);
48 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI0A_L0_T0AB_EQ_IS
, 1);
49 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI0A_L0_T0AB_EQ_BW
, 1);
50 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI0A_L1_T1AB_EQ_IS
, 1);
51 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI0A_L1_T1AB_EQ_BW
, 1);
52 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA20_CSI0A
, RG_CSI0A_L2_T1BC_EQ_IS
, 1);
53 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA20_CSI0A
, RG_CSI0A_L2_T1BC_EQ_BW
, 1);
56 static void mtk_phy_csi_dphy_ana_eq_tune(void __iomem
*base
)
58 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L0_EQ_IS
, 1);
59 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L0_EQ_BW
, 1);
60 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L1_EQ_IS
, 1);
61 mtk_phy_update_field(base
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L1_EQ_BW
, 1);
62 mtk_phy_update_field(base
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI1A_L2_EQ_IS
, 1);
63 mtk_phy_update_field(base
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI1A_L2_EQ_BW
, 1);
65 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L0_EQ_IS
, 1);
66 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L0_EQ_BW
, 1);
67 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L1_EQ_IS
, 1);
68 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA18_CSIXA
, RG_CSI1A_L1_EQ_BW
, 1);
69 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI1A_L2_EQ_IS
, 1);
70 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA1C_CSIXA
, RG_CSI1A_L2_EQ_BW
, 1);
73 static int mtk_mipi_phy_power_on(struct phy
*phy
)
75 struct mtk_mipi_cdphy_port
*port
= phy_get_drvdata(phy
);
76 void __iomem
*base
= port
->base
;
79 * The driver currently supports DPHY and CD-PHY phys,
80 * but the only mode supported is DPHY,
81 * so CD-PHY capable phys must be configured in DPHY mode
83 if (port
->type
== CDPHY
) {
84 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSI0A_CPHY_EN
, 0);
85 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
,
92 * Only 4 data + 1 clock is supported for now with the following mapping:
100 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L0_CKMODE_EN
, 0);
101 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L0_CKSEL
, 1);
102 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L1_CKMODE_EN
, 0);
103 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L1_CKSEL
, 1);
104 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L2_CKMODE_EN
, 1);
105 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L2_CKSEL
, 1);
107 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
,
108 RG_CSIXA_DPHY_L0_CKMODE_EN
, 0);
109 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L0_CKSEL
, 1);
110 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
,
111 RG_CSIXA_DPHY_L1_CKMODE_EN
, 0);
112 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L1_CKSEL
, 1);
113 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
,
114 RG_CSIXA_DPHY_L2_CKMODE_EN
, 0);
115 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_DPHY_L2_CKSEL
, 1);
117 /* Byte clock invert */
118 mtk_phy_update_field(base
+ MIPI_RX_ANAA8_CSIXA
, RG_CSIXA_CDPHY_L0_T0_BYTECK_INVERT
, 1);
119 mtk_phy_update_field(base
+ MIPI_RX_ANAA8_CSIXA
, RG_CSIXA_DPHY_L1_BYTECK_INVERT
, 1);
120 mtk_phy_update_field(base
+ MIPI_RX_ANAA8_CSIXA
, RG_CSIXA_CDPHY_L2_T1_BYTECK_INVERT
, 1);
122 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANAA8_CSIXA
,
123 RG_CSIXA_CDPHY_L0_T0_BYTECK_INVERT
, 1);
124 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANAA8_CSIXA
,
125 RG_CSIXA_DPHY_L1_BYTECK_INVERT
, 1);
126 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANAA8_CSIXA
,
127 RG_CSIXA_CDPHY_L2_T1_BYTECK_INVERT
, 1);
129 /* Start ANA EQ tuning */
130 if (port
->type
== CDPHY
)
131 mtk_phy_csi_cdphy_ana_eq_tune(base
);
133 mtk_phy_csi_dphy_ana_eq_tune(base
);
135 /* End ANA EQ tuning */
136 mtk_phy_set_bits(base
+ MIPI_RX_ANA40_CSIXA
, 0x90);
138 mtk_phy_update_field(base
+ MIPI_RX_ANA24_CSIXA
, RG_CSIXA_RESERVE
, 0x40);
139 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA24_CSIXA
, RG_CSIXA_RESERVE
, 0x40);
140 mtk_phy_update_field(base
+ MIPI_RX_WRAPPER80_CSIXA
, CSR_CSI_RST_MODE
, 0);
141 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_WRAPPER80_CSIXA
, CSR_CSI_RST_MODE
, 0);
143 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_CORE_EN
, 1);
144 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_CORE_EN
, 1);
145 usleep_range(20, 40);
146 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_LPF_EN
, 1);
147 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_LPF_EN
, 1);
152 static int mtk_mipi_phy_power_off(struct phy
*phy
)
154 struct mtk_mipi_cdphy_port
*port
= phy_get_drvdata(phy
);
155 void __iomem
*base
= port
->base
;
157 /* Disable MIPI BG. */
158 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_CORE_EN
, 0);
159 mtk_phy_update_field(base
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_LPF_EN
, 0);
161 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_CORE_EN
, 0);
162 mtk_phy_update_field(base
+ CSIXB_OFFSET
+ MIPI_RX_ANA00_CSIXA
, RG_CSIXA_BG_LPF_EN
, 0);
167 static struct phy
*mtk_mipi_cdphy_xlate(struct device
*dev
,
168 const struct of_phandle_args
*args
)
170 struct mtk_mipi_cdphy_port
*priv
= dev_get_drvdata(dev
);
173 * If PHY is CD-PHY then we need to get the operating mode
174 * For now only D-PHY mode is supported
176 if (priv
->type
== CDPHY
) {
177 if (args
->args_count
!= 1) {
178 dev_err(dev
, "invalid number of arguments\n");
179 return ERR_PTR(-EINVAL
);
181 switch (args
->args
[0]) {
184 if (priv
->num_lanes
!= 4) {
185 dev_err(dev
, "Only 4D1C mode is supported for now!\n");
186 return ERR_PTR(-EINVAL
);
190 dev_err(dev
, "Unsupported PHY type: %i\n", args
->args
[0]);
191 return ERR_PTR(-EINVAL
);
194 if (args
->args_count
) {
195 dev_err(dev
, "invalid number of arguments\n");
196 return ERR_PTR(-EINVAL
);
204 static const struct phy_ops mtk_cdphy_ops
= {
205 .power_on
= mtk_mipi_phy_power_on
,
206 .power_off
= mtk_mipi_phy_power_off
,
207 .owner
= THIS_MODULE
,
210 static int mtk_mipi_cdphy_probe(struct platform_device
*pdev
)
212 struct device
*dev
= &pdev
->dev
;
213 struct phy_provider
*phy_provider
;
214 struct mtk_mipi_cdphy_port
*port
;
219 port
= devm_kzalloc(dev
, sizeof(*port
), GFP_KERNEL
);
223 dev_set_drvdata(dev
, port
);
227 port
->base
= devm_platform_ioremap_resource(pdev
, 0);
228 if (IS_ERR(port
->base
))
229 return PTR_ERR(port
->base
);
231 ret
= of_property_read_u32(dev
->of_node
, "num-lanes", &port
->num_lanes
);
233 dev_err(dev
, "Failed to read num-lanes property: %i\n", ret
);
238 * phy-type is optional, if not present, PHY is considered to be CD-PHY
240 if (device_property_present(dev
, "phy-type")) {
241 ret
= of_property_read_u32(dev
->of_node
, "phy-type", &phy_type
);
243 dev_err(dev
, "Failed to read phy-type property: %i\n", ret
);
251 dev_err(dev
, "Unsupported PHY type: %i\n", phy_type
);
258 phy
= devm_phy_create(dev
, NULL
, &mtk_cdphy_ops
);
260 dev_err(dev
, "Failed to create PHY: %ld\n", PTR_ERR(phy
));
265 phy_set_drvdata(phy
, port
);
267 phy_provider
= devm_of_phy_provider_register(dev
, mtk_mipi_cdphy_xlate
);
268 if (IS_ERR(phy_provider
)) {
269 dev_err(dev
, "Failed to register PHY provider: %ld\n",
270 PTR_ERR(phy_provider
));
271 return PTR_ERR(phy_provider
);
277 static const struct of_device_id mtk_mipi_cdphy_of_match
[] = {
278 { .compatible
= "mediatek,mt8365-csi-rx" },
281 MODULE_DEVICE_TABLE(of
, mtk_mipi_cdphy_of_match
);
283 static struct platform_driver mipi_cdphy_pdrv
= {
284 .probe
= mtk_mipi_cdphy_probe
,
286 .name
= "mtk-mipi-csi-0-5",
287 .of_match_table
= mtk_mipi_cdphy_of_match
,
290 module_platform_driver(mipi_cdphy_pdrv
);
292 MODULE_DESCRIPTION("MediaTek MIPI CSI CD-PHY v0.5 Driver");
293 MODULE_AUTHOR("Louis Kuo <louis.kuo@mediatek.com>");
294 MODULE_LICENSE("GPL");