File Index
Files and directories
Sign in
FILE

dphy.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * RP1 CSI-2 Driver
  4 *
  5 * Copyright (c) 2021-2024 Raspberry Pi Ltd.
  6 * Copyright (c) 2023-2024 Ideas on Board Oy
  7 */
  8
  9#include <linux/delay.h>
 10#include <linux/pm_runtime.h>
 11
 12#include "dphy.h"
 13
 14#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg)
 15#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg)
 16
 17/* DW dphy Host registers */
 18#define DPHY_VERSION		0x000
 19#define DPHY_N_LANES		0x004
 20#define DPHY_RESETN		0x008
 21#define DPHY_PHY_SHUTDOWNZ	0x040
 22#define DPHY_PHY_RSTZ		0x044
 23#define DPHY_PHY_RX		0x048
 24#define	DPHY_PHY_STOPSTATE	0x04c
 25#define DPHY_PHY_TST_CTRL0	0x050
 26#define DPHY_PHY_TST_CTRL1	0x054
 27#define DPHY_PHY2_TST_CTRL0	0x058
 28#define DPHY_PHY2_TST_CTRL1	0x05c
 29
 30/* DW dphy Host Transactions */
 31#define DPHY_HS_RX_CTRL_LANE0_OFFSET	0x44
 32#define DPHY_PLL_INPUT_DIV_OFFSET	0x17
 33#define DPHY_PLL_LOOP_DIV_OFFSET	0x18
 34#define DPHY_PLL_DIV_CTRL_OFFSET	0x19
 35
 36static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset)
 37{
 38	return readl(dphy->base + offset);
 39}
 40
 41static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
 42{
 43	writel(data, dphy->base + offset);
 44}
 45
 46static void set_tstclr(struct dphy_data *dphy, u32 val)
 47{
 48	u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
 49
 50	dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val);
 51}
 52
 53static void set_tstclk(struct dphy_data *dphy, u32 val)
 54{
 55	u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
 56
 57	dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1));
 58}
 59
 60static uint8_t get_tstdout(struct dphy_data *dphy)
 61{
 62	u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
 63
 64	return ((ctrl1 >> 8) & 0xff);
 65}
 66
 67static void set_testen(struct dphy_data *dphy, u32 val)
 68{
 69	u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
 70
 71	dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1,
 72			   (ctrl1 & ~(1 << 16)) | (val << 16));
 73}
 74
 75static void set_testdin(struct dphy_data *dphy, u32 val)
 76{
 77	u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
 78
 79	dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val);
 80}
 81
 82static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code,
 83				uint8_t test_data)
 84{
 85	/* See page 101 of the MIPI DPHY databook. */
 86	set_tstclk(dphy, 1);
 87	set_testen(dphy, 0);
 88	set_testdin(dphy, test_code);
 89	set_testen(dphy, 1);
 90	set_tstclk(dphy, 0);
 91	set_testen(dphy, 0);
 92	set_testdin(dphy, test_data);
 93	set_tstclk(dphy, 1);
 94	return get_tstdout(dphy);
 95}
 96
 97static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps)
 98{
 99	/* See Table 5-1 on page 65 of dphy databook */
100	static const u16 hsfreqrange_table[][2] = {
101		{ 89, 0b000000 },   { 99, 0b010000 },	{ 109, 0b100000 },
102		{ 129, 0b000001 },  { 139, 0b010001 },	{ 149, 0b100001 },
103		{ 169, 0b000010 },  { 179, 0b010010 },	{ 199, 0b100010 },
104		{ 219, 0b000011 },  { 239, 0b010011 },	{ 249, 0b100011 },
105		{ 269, 0b000100 },  { 299, 0b010100 },	{ 329, 0b000101 },
106		{ 359, 0b010101 },  { 399, 0b100101 },	{ 449, 0b000110 },
107		{ 499, 0b010110 },  { 549, 0b000111 },	{ 599, 0b010111 },
108		{ 649, 0b001000 },  { 699, 0b011000 },	{ 749, 0b001001 },
109		{ 799, 0b011001 },  { 849, 0b101001 },	{ 899, 0b111001 },
110		{ 949, 0b001010 },  { 999, 0b011010 },	{ 1049, 0b101010 },
111		{ 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 },
112		{ 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 },
113		{ 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 },
114	};
115	unsigned int i;
116
117	if (mbps < 80 || mbps > 1500)
118		dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps);
119
120	for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
121		if (mbps <= hsfreqrange_table[i][0])
122			break;
123	}
124
125	dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET,
126			 hsfreqrange_table[i][1] << 1);
127}
128
129static void dphy_init(struct dphy_data *dphy)
130{
131	dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0);
132	dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0);
133	set_tstclk(dphy, 1);
134	set_testen(dphy, 0);
135	set_tstclr(dphy, 1);
136	usleep_range(15, 20);
137	set_tstclr(dphy, 0);
138	usleep_range(15, 20);
139
140	dphy_set_hsfreqrange(dphy, dphy->dphy_rate);
141
142	usleep_range(5, 10);
143	dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1);
144	usleep_range(5, 10);
145	dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1);
146}
147
148void dphy_start(struct dphy_data *dphy)
149{
150	dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__,
151		 dphy->dphy_rate, dphy->active_lanes);
152
153	dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1));
154	dphy_init(dphy);
155	dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff);
156	usleep_range(10, 50);
157}
158
159void dphy_stop(struct dphy_data *dphy)
160{
161	dphy_dbg(dphy, "%s\n", __func__);
162
163	/* Set only one lane (lane 0) as active (ON) */
164	dw_csi2_host_write(dphy, DPHY_N_LANES, 0);
165	dw_csi2_host_write(dphy, DPHY_RESETN, 0);
166}
167
168void dphy_probe(struct dphy_data *dphy)
169{
170	u32 host_ver;
171	u8 host_ver_major, host_ver_minor;
172
173	host_ver = dw_csi2_host_read(dphy, DPHY_VERSION);
174	host_ver_major = (u8)((host_ver >> 24) - '0');
175	host_ver_minor = (u8)((host_ver >> 16) - '0');
176	host_ver_minor = host_ver_minor * 10;
177	host_ver_minor += (u8)((host_ver >> 8) - '0');
178
179	dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major,
180		 host_ver_minor);
181}
~/ Read-only file preview.