Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / media / pci / intel / ipu6 / ipu6-isys-jsl-phy.c
blobc804291cfae924089fd069418750690b83d4c4c4
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2013--2024 Intel Corporation
4 */
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/device.h>
9 #include <linux/io.h>
11 #include "ipu6-bus.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<-----| | | |
29 * | | | port | | |
30 * | port1 x2<-----| | | |
31 * | | | <-| PHY |
32 * | port2 x4<-----| | | |
33 * | | |config| | |
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] = {
50 /* sip0 */
51 {0, 1, 0, 0x15},
52 {0, 2, 0, 0x15},
53 {0, 4, 0, 0x15},
54 {0, 4, 2, 0x22},
55 /* sip1 */
56 {2, 1, 4, 0x15},
57 {2, 2, 4, 0x15},
58 {2, 4, 4, 0x15},
59 {2, 4, 6, 0x22}
62 static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys,
63 unsigned int port,
64 unsigned int nlanes)
66 struct device *dev = &isys->adev->auxdev.dev;
67 void __iomem *base = isys->adev->isp->base;
68 unsigned int bbnum;
69 u32 val, reg, i;
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);
84 val |= BIT(31);
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);
96 val |= BIT(0);
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;
116 u32 val, reg;
118 reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL;
119 val = readl(base + reg);
120 val |= BIT(0);
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);
125 val |= BIT(0);
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);
130 val |= BIT(0);
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);
135 val |= BIT(0);
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;
144 unsigned int index;
146 switch (nlanes) {
147 case 1:
148 index = 5;
149 break;
150 case 2:
151 index = 6;
152 break;
153 case 4:
154 index = 1;
155 break;
156 default:
157 dev_err(dev, "lanes nr %u is unsupported\n", nlanes);
158 return -EINVAL;
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));
166 return 0;
169 static void
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;
175 void __iomem *reg;
176 u32 port_base;
177 u32 i;
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,
208 bool on)
210 struct device *dev = &isys->adev->auxdev.dev;
211 void __iomem *isys_base = isys->pdata->base;
212 int ret = 0;
213 u32 nlanes;
214 u32 port;
216 if (!on)
217 return 0;
219 port = cfg->port;
220 nlanes = cfg->nlanes;
222 if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
223 dev_warn(dev, "invalid port ID %d\n", port);
224 return -EINVAL;
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);
236 if (ret)
237 return ret;
239 ipu6_isys_csi2_rx_control(isys);
241 return 0;