1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013--2024 Intel Corporation 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/bits.h> 8 #include <linux/device.h> 9 #include <linux/io.h> 10 11 #include "ipu6-bus.h" 12 #include "ipu6-isys.h" 13 #include "ipu6-isys-csi2.h" 14 #include "ipu6-platform-isys-csi2-reg.h" 15 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 19 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)) 24 25 /* 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 * +---------+ +------+ +-----+ 36 */ 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 */ 46 }; 47 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} 60 }; 61 62 static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys, 63 unsigned int port, 64 unsigned int nlanes) 65 { 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; 70 71 dev_dbg(dev, "port %u with %u lanes", port, nlanes); 72 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); 80 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); 86 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); 92 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); 98 } 99 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); 109 } 110 } 111 } 112 113 static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys) 114 { 115 void __iomem *base = isys->adev->isp->base; 116 u32 val, reg; 117 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); 122 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); 127 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); 132 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); 137 } 138 139 static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys, 140 unsigned int port, unsigned int nlanes) 141 { 142 struct device *dev = &isys->adev->auxdev.dev; 143 unsigned int sip = port / 2; 144 unsigned int index; 145 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; 159 } 160 161 dev_dbg(dev, "port config for port %u with %u lanes\n", port, nlanes); 162 163 writel(csi2_port_cfg[index][2], 164 isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip)); 165 166 return 0; 167 } 168 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) 173 { 174 struct device *dev = &isys->adev->auxdev.dev; 175 void __iomem *reg; 176 u32 port_base; 177 u32 i; 178 179 port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) : 180 CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port); 181 182 dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes); 183 184 reg = isys->pdata->base + port_base; 185 reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE; 186 187 writel(timing->ctermen, reg); 188 189 reg = isys->pdata->base + port_base; 190 reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE; 191 writel(timing->csettle, reg); 192 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); 197 198 reg = isys->pdata->base + port_base; 199 reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i); 200 writel(timing->dsettle, reg); 201 } 202 } 203 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) 209 { 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; 215 216 if (!on) 217 return 0; 218 219 port = cfg->port; 220 nlanes = cfg->nlanes; 221 222 if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { 223 dev_warn(dev, "invalid port ID %d\n", port); 224 return -EINVAL; 225 } 226 227 ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes); 228 229 writel(DPHY_TIMER_INCR, 230 isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR); 231 232 /* set port cfg and rx timing */ 233 ipu6_isys_csi2_set_timing(isys, timing, port, nlanes); 234 235 ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes); 236 if (ret) 237 return ret; 238 239 ipu6_isys_csi2_rx_control(isys); 240 241 return 0; 242 } 243