brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · c804291 Raw
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