243 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */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 418#define IPU6SE_ISYS_PHY_0_BASE 0x1000019 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 used27 * +---------+ +------+ +-----+28 * | port0 x4<-----| | | |29 * | | | port | | |30 * | port1 x2<-----| | | |31 * | | | <-| PHY |32 * | port2 x4<-----| | | |33 * | | |config| | |34 * | port3 x2<-----| | | |35 * +---------+ +------+ +-----+36 */37static 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 */49static 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 62static 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 113static 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 139static 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 169static void170ipu6_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 0x28205int 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