162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "mipi-phy.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Default D-PHY timings based on MIPI D-PHY specification. Derived from the
1362306a36Sopenharmony_ci * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
1462306a36Sopenharmony_ci * specification (v1.2) with minor adjustments.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ciint mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
1762306a36Sopenharmony_ci				 unsigned long period)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	timing->clkmiss = 0;
2062306a36Sopenharmony_ci	timing->clkpost = 70 + 52 * period;
2162306a36Sopenharmony_ci	timing->clkpre = 8;
2262306a36Sopenharmony_ci	timing->clkprepare = 65;
2362306a36Sopenharmony_ci	timing->clksettle = 95;
2462306a36Sopenharmony_ci	timing->clktermen = 0;
2562306a36Sopenharmony_ci	timing->clktrail = 80;
2662306a36Sopenharmony_ci	timing->clkzero = 260;
2762306a36Sopenharmony_ci	timing->dtermen = 0;
2862306a36Sopenharmony_ci	timing->eot = 0;
2962306a36Sopenharmony_ci	timing->hsexit = 120;
3062306a36Sopenharmony_ci	timing->hsprepare = 65 + 5 * period;
3162306a36Sopenharmony_ci	timing->hszero = 145 + 5 * period;
3262306a36Sopenharmony_ci	timing->hssettle = 85 + 6 * period;
3362306a36Sopenharmony_ci	timing->hsskip = 40;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
3762306a36Sopenharmony_ci	 * contains this formula as:
3862306a36Sopenharmony_ci	 *
3962306a36Sopenharmony_ci	 *     T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period)
4062306a36Sopenharmony_ci	 *
4162306a36Sopenharmony_ci	 * where n = 1 for forward-direction HS mode and n = 4 for reverse-
4262306a36Sopenharmony_ci	 * direction HS mode. There's only one setting and this function does
4362306a36Sopenharmony_ci	 * not parameterize on anything other that period, so this code will
4462306a36Sopenharmony_ci	 * assumes that reverse-direction HS mode is supported and uses n = 4.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci	timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	timing->init = 100000;
4962306a36Sopenharmony_ci	timing->lpx = 60;
5062306a36Sopenharmony_ci	timing->taget = 5 * timing->lpx;
5162306a36Sopenharmony_ci	timing->tago = 4 * timing->lpx;
5262306a36Sopenharmony_ci	timing->tasure = 2 * timing->lpx;
5362306a36Sopenharmony_ci	timing->wakeup = 1000000;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Validate D-PHY timing according to MIPI D-PHY specification (v1.2, Section
6062306a36Sopenharmony_ci * Section 6.9 "Global Operation Timing Parameters").
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ciint mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
6362306a36Sopenharmony_ci			      unsigned long period)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	if (timing->clkmiss > 60)
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (timing->clkpost < (60 + 52 * period))
6962306a36Sopenharmony_ci		return -EINVAL;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (timing->clkpre < 8)
7262306a36Sopenharmony_ci		return -EINVAL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (timing->clkprepare < 38 || timing->clkprepare > 95)
7562306a36Sopenharmony_ci		return -EINVAL;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (timing->clksettle < 95 || timing->clksettle > 300)
7862306a36Sopenharmony_ci		return -EINVAL;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (timing->clktermen > 38)
8162306a36Sopenharmony_ci		return -EINVAL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (timing->clktrail < 60)
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (timing->clkprepare + timing->clkzero < 300)
8762306a36Sopenharmony_ci		return -EINVAL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (timing->dtermen > 35 + 4 * period)
9062306a36Sopenharmony_ci		return -EINVAL;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (timing->eot > 105 + 12 * period)
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (timing->hsexit < 100)
9662306a36Sopenharmony_ci		return -EINVAL;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (timing->hsprepare < 40 + 4 * period ||
9962306a36Sopenharmony_ci	    timing->hsprepare > 85 + 6 * period)
10062306a36Sopenharmony_ci		return -EINVAL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (timing->hsprepare + timing->hszero < 145 + 10 * period)
10362306a36Sopenharmony_ci		return -EINVAL;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if ((timing->hssettle < 85 + 6 * period) ||
10662306a36Sopenharmony_ci	    (timing->hssettle > 145 + 10 * period))
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period)
11062306a36Sopenharmony_ci		return -EINVAL;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (timing->hstrail < max(8 * period, 60 + 4 * period))
11362306a36Sopenharmony_ci		return -EINVAL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (timing->init < 100000)
11662306a36Sopenharmony_ci		return -EINVAL;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (timing->lpx < 50)
11962306a36Sopenharmony_ci		return -EINVAL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (timing->taget != 5 * timing->lpx)
12262306a36Sopenharmony_ci		return -EINVAL;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (timing->tago != 4 * timing->lpx)
12562306a36Sopenharmony_ci		return -EINVAL;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx)
12862306a36Sopenharmony_ci		return -EINVAL;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (timing->wakeup < 1000000)
13162306a36Sopenharmony_ci		return -EINVAL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
135