File Index
Files and directories
Sign in
FILE

ipu6-isys-jsl-phy.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c C
  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 */
 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 void
170ipu6_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
205int 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}
~/ Read-only file preview.