188 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2/*3 * Copyright (C) 2013 NVIDIA Corporation4 * Copyright (C) 2018 Cadence Design Systems Inc.5 */6 7#include <linux/errno.h>8#include <linux/export.h>9#include <linux/kernel.h>10#include <linux/time64.h>11 12#include <linux/phy/phy.h>13#include <linux/phy/phy-mipi-dphy.h>14 15/*16 * Minimum D-PHY timings based on MIPI D-PHY specification. Derived17 * from the valid ranges specified in Section 6.9, Table 14, Page 4118 * of the D-PHY specification (v1.2).19 */20static int phy_mipi_dphy_calc_config(unsigned long pixel_clock,21 unsigned int bpp,22 unsigned int lanes,23 unsigned long long hs_clk_rate,24 struct phy_configure_opts_mipi_dphy *cfg)25{26 unsigned long long ui;27 28 if (!cfg)29 return -EINVAL;30 31 if (!hs_clk_rate) {32 hs_clk_rate = pixel_clock * bpp;33 do_div(hs_clk_rate, lanes);34 }35 36 ui = ALIGN(PSEC_PER_SEC, hs_clk_rate);37 do_div(ui, hs_clk_rate);38 39 cfg->clk_miss = 0;40 cfg->clk_post = 60000 + 52 * ui;41 cfg->clk_pre = 8;42 cfg->clk_prepare = 38000;43 cfg->clk_settle = 95000;44 cfg->clk_term_en = 0;45 cfg->clk_trail = 60000;46 cfg->clk_zero = 262000;47 cfg->d_term_en = 0;48 cfg->eot = 0;49 cfg->hs_exit = 100000;50 cfg->hs_prepare = 40000 + 4 * ui;51 cfg->hs_zero = 105000 + 6 * ui;52 cfg->hs_settle = 85000 + 6 * ui;53 cfg->hs_skip = 40000;54 55 /*56 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)57 * contains this formula as:58 *59 * T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)60 *61 * where n = 1 for forward-direction HS mode and n = 4 for reverse-62 * direction HS mode. There's only one setting and this function does63 * not parameterize on anything other that ui, so this code will64 * assumes that reverse-direction HS mode is supported and uses n = 4.65 */66 cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);67 68 cfg->init = 100;69 cfg->lpx = 50000;70 cfg->ta_get = 5 * cfg->lpx;71 cfg->ta_go = 4 * cfg->lpx;72 cfg->ta_sure = cfg->lpx;73 cfg->wakeup = 1000;74 75 cfg->hs_clk_rate = hs_clk_rate;76 cfg->lanes = lanes;77 78 return 0;79}80 81int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,82 unsigned int bpp,83 unsigned int lanes,84 struct phy_configure_opts_mipi_dphy *cfg)85{86 return phy_mipi_dphy_calc_config(pixel_clock, bpp, lanes, 0, cfg);87 88}89EXPORT_SYMBOL(phy_mipi_dphy_get_default_config);90 91int phy_mipi_dphy_get_default_config_for_hsclk(unsigned long long hs_clk_rate,92 unsigned int lanes,93 struct phy_configure_opts_mipi_dphy *cfg)94{95 if (!hs_clk_rate)96 return -EINVAL;97 98 return phy_mipi_dphy_calc_config(0, 0, lanes, hs_clk_rate, cfg);99 100}101EXPORT_SYMBOL(phy_mipi_dphy_get_default_config_for_hsclk);102 103/*104 * Validate D-PHY configuration according to MIPI D-PHY specification105 * (v1.2, Section Section 6.9 "Global Operation Timing Parameters").106 */107int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)108{109 unsigned long long ui;110 111 if (!cfg)112 return -EINVAL;113 114 ui = ALIGN(PSEC_PER_SEC, cfg->hs_clk_rate);115 do_div(ui, cfg->hs_clk_rate);116 117 if (cfg->clk_miss > 60000)118 return -EINVAL;119 120 if (cfg->clk_post < (60000 + 52 * ui))121 return -EINVAL;122 123 if (cfg->clk_pre < 8)124 return -EINVAL;125 126 if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000)127 return -EINVAL;128 129 if (cfg->clk_settle < 95000 || cfg->clk_settle > 300000)130 return -EINVAL;131 132 if (cfg->clk_term_en > 38000)133 return -EINVAL;134 135 if (cfg->clk_trail < 60000)136 return -EINVAL;137 138 if ((cfg->clk_prepare + cfg->clk_zero) < 300000)139 return -EINVAL;140 141 if (cfg->d_term_en > (35000 + 4 * ui))142 return -EINVAL;143 144 if (cfg->eot > (105000 + 12 * ui))145 return -EINVAL;146 147 if (cfg->hs_exit < 100000)148 return -EINVAL;149 150 if (cfg->hs_prepare < (40000 + 4 * ui) ||151 cfg->hs_prepare > (85000 + 6 * ui))152 return -EINVAL;153 154 if ((cfg->hs_prepare + cfg->hs_zero) < (145000 + 10 * ui))155 return -EINVAL;156 157 if ((cfg->hs_settle < (85000 + 6 * ui)) ||158 (cfg->hs_settle > (145000 + 10 * ui)))159 return -EINVAL;160 161 if (cfg->hs_skip < 40000 || cfg->hs_skip > (55000 + 4 * ui))162 return -EINVAL;163 164 if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))165 return -EINVAL;166 167 if (cfg->init < 100)168 return -EINVAL;169 170 if (cfg->lpx < 50000)171 return -EINVAL;172 173 if (cfg->ta_get != (5 * cfg->lpx))174 return -EINVAL;175 176 if (cfg->ta_go != (4 * cfg->lpx))177 return -EINVAL;178 179 if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))180 return -EINVAL;181 182 if (cfg->wakeup < 1000)183 return -EINVAL;184 185 return 0;186}187EXPORT_SYMBOL(phy_mipi_dphy_config_validate);188