1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2013--2024 Intel Corporation
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/device.h>
12 #include "ipu6-isys.h"
13 #include "ipu6-isys-csi2.h"
14 #include "ipu6-platform-isys-csi2-reg.h"
16 /* only use BB0, BB2, BB4, and BB6 on PHY0 */
17 #define IPU6SE_ISYS_PHY_BB_NUM 4
18 #define IPU6SE_ISYS_PHY_0_BASE 0x10000
20 #define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x))
21 #define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x))
22 #define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x))
23 #define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x))
26 * use port_cfg to configure that which data lanes used
27 * +---------+ +------+ +-----+
28 * | port0 x4<-----| | | |
30 * | port1 x2<-----| | | |
32 * | port2 x4<-----| | | |
34 * | port3 x2<-----| | | |
35 * +---------+ +------+ +-----+
37 static const unsigned int csi2_port_cfg
[][3] = {
38 {0, 0, 0x1f}, /* no link */
39 {4, 0, 0x10}, /* x4 + x4 config */
40 {2, 0, 0x12}, /* x2 + x2 config */
41 {1, 0, 0x13}, /* x1 + x1 config */
42 {2, 1, 0x15}, /* x2x1 + x2x1 config */
43 {1, 1, 0x16}, /* x1x1 + x1x1 config */
44 {2, 2, 0x18}, /* x2x2 + x2x2 config */
45 {1, 2, 0x19} /* x1x2 + x1x2 config */
48 /* port, nlanes, bbindex, portcfg */
49 static const unsigned int phy_port_cfg
[][4] = {
62 static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys
*isys
,
66 struct device
*dev
= &isys
->adev
->auxdev
.dev
;
67 void __iomem
*base
= isys
->adev
->isp
->base
;
71 dev_dbg(dev
, "port %u with %u lanes", port
, nlanes
);
73 /* only support <1.5Gbps */
74 for (i
= 0; i
< IPU6SE_ISYS_PHY_BB_NUM
; i
++) {
75 /* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */
76 reg
= IPU6SE_ISYS_PHY_0_BASE
+ PHY_CPHY_DLL_OVRD(i
);
77 val
= readl(base
+ reg
);
78 val
|= FIELD_PREP(GENMASK(6, 1), 13);
79 writel(val
, base
+ reg
);
81 /* cphy_rx_control1.en_crc1 = 1 */
82 reg
= IPU6SE_ISYS_PHY_0_BASE
+ PHY_CPHY_RX_CONTROL1(i
);
83 val
= readl(base
+ reg
);
85 writel(val
, base
+ reg
);
87 /* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */
88 reg
= IPU6SE_ISYS_PHY_0_BASE
+ PHY_DPHY_CFG(i
);
89 val
= readl(base
+ reg
);
90 val
|= BIT(25) | BIT(26);
91 writel(val
, base
+ reg
);
93 /* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */
94 reg
= IPU6SE_ISYS_PHY_0_BASE
+ PHY_CPHY_DLL_OVRD(i
);
95 val
= readl(base
+ reg
);
97 writel(val
, base
+ reg
);
100 /* Front end config, use minimal channel loss */
101 for (i
= 0; i
< ARRAY_SIZE(phy_port_cfg
); i
++) {
102 if (phy_port_cfg
[i
][0] == port
&&
103 phy_port_cfg
[i
][1] == nlanes
) {
104 bbnum
= phy_port_cfg
[i
][2] / 2;
105 reg
= IPU6SE_ISYS_PHY_0_BASE
+ PHY_BB_AFE_CONFIG(bbnum
);
106 val
= readl(base
+ reg
);
107 val
|= phy_port_cfg
[i
][3];
108 writel(val
, base
+ reg
);
113 static void ipu6_isys_csi2_rx_control(struct ipu6_isys
*isys
)
115 void __iomem
*base
= isys
->adev
->isp
->base
;
118 reg
= CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL
;
119 val
= readl(base
+ reg
);
121 writel(val
, base
+ CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL
);
123 reg
= CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL
;
124 val
= readl(base
+ reg
);
126 writel(val
, base
+ CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL
);
128 reg
= CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL
;
129 val
= readl(base
+ reg
);
131 writel(val
, base
+ CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL
);
133 reg
= CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL
;
134 val
= readl(base
+ reg
);
136 writel(val
, base
+ CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL
);
139 static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys
*isys
,
140 unsigned int port
, unsigned int nlanes
)
142 struct device
*dev
= &isys
->adev
->auxdev
.dev
;
143 unsigned int sip
= port
/ 2;
157 dev_err(dev
, "lanes nr %u is unsupported\n", nlanes
);
161 dev_dbg(dev
, "port config for port %u with %u lanes\n", port
, nlanes
);
163 writel(csi2_port_cfg
[index
][2],
164 isys
->pdata
->base
+ CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip
));
170 ipu6_isys_csi2_set_timing(struct ipu6_isys
*isys
,
171 const struct ipu6_isys_csi2_timing
*timing
,
172 unsigned int port
, unsigned int nlanes
)
174 struct device
*dev
= &isys
->adev
->auxdev
.dev
;
179 port_base
= (port
% 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port
) :
180 CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port
);
182 dev_dbg(dev
, "set timing for port %u with %u lanes\n", port
, nlanes
);
184 reg
= isys
->pdata
->base
+ port_base
;
185 reg
+= CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE
;
187 writel(timing
->ctermen
, reg
);
189 reg
= isys
->pdata
->base
+ port_base
;
190 reg
+= CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE
;
191 writel(timing
->csettle
, reg
);
193 for (i
= 0; i
< nlanes
; i
++) {
194 reg
= isys
->pdata
->base
+ port_base
;
195 reg
+= CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i
);
196 writel(timing
->dtermen
, reg
);
198 reg
= isys
->pdata
->base
+ port_base
;
199 reg
+= CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i
);
200 writel(timing
->dsettle
, reg
);
204 #define DPHY_TIMER_INCR 0x28
205 int ipu6_isys_jsl_phy_set_power(struct ipu6_isys
*isys
,
206 struct ipu6_isys_csi2_config
*cfg
,
207 const struct ipu6_isys_csi2_timing
*timing
,
210 struct device
*dev
= &isys
->adev
->auxdev
.dev
;
211 void __iomem
*isys_base
= isys
->pdata
->base
;
220 nlanes
= cfg
->nlanes
;
222 if (!isys_base
|| port
>= isys
->pdata
->ipdata
->csi2
.nports
) {
223 dev_warn(dev
, "invalid port ID %d\n", port
);
227 ipu6_isys_csi2_phy_config_by_port(isys
, port
, nlanes
);
229 writel(DPHY_TIMER_INCR
,
230 isys
->pdata
->base
+ CSI2_HUB_GPREG_DPHY_TIMER_INCR
);
232 /* set port cfg and rx timing */
233 ipu6_isys_csi2_set_timing(isys
, timing
, port
, nlanes
);
235 ret
= ipu6_isys_csi2_set_port_cfg(isys
, port
, nlanes
);
239 ipu6_isys_csi2_rx_control(isys
);