269 lines
7.3 KiB
C
269 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* Author: Shunqing Chen <csq@rock-chips.com>
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include "rk628.h"
|
|
#include "rk628_combtxphy.h"
|
|
#include "rk628_cru.h"
|
|
|
|
void rk628_txphy_set_bus_width(struct rk628 *rk628, u32 bus_width)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
txphy->bus_width = bus_width;
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_set_bus_width);
|
|
|
|
u32 rk628_txphy_get_bus_width(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
return txphy->bus_width;
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_get_bus_width);
|
|
|
|
static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_BUS_WIDTH_MASK |
|
|
SW_GVI_LVDS_EN_MASK |
|
|
SW_MIPI_DSI_EN_MASK,
|
|
SW_BUS_WIDTH_8BIT | SW_MIPI_DSI_EN);
|
|
|
|
if (txphy->flags & COMBTXPHY_MODULEA_EN)
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_MODULEA_EN_MASK,
|
|
SW_MODULEA_EN);
|
|
if (txphy->flags & COMBTXPHY_MODULEB_EN)
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_MODULEB_EN_MASK,
|
|
SW_MODULEB_EN);
|
|
|
|
rk628_i2c_write(rk628, COMBTXPHY_CON5, SW_REF_DIV(txphy->ref_div - 1) |
|
|
SW_PLL_FB_DIV(txphy->fb_div) |
|
|
SW_PLL_FRAC_DIV(txphy->frac_div) |
|
|
SW_RATE(txphy->rate_div / 2));
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, 0);
|
|
usleep_range(100, 200);
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON9, SW_DSI_FSET_EN_MASK |
|
|
SW_DSI_RCAL_EN_MASK | SW_LPTX_SR_TRIM_MASK, SW_DSI_FSET_EN |
|
|
SW_DSI_RCAL_EN | SW_LPTX_SR_TRIM(7));
|
|
if (rk628->tx_mode && rk628->dual_mipi)
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON6, SW_PLL_CTRL0_MASK, SW_PLL_CTRL0(1));
|
|
|
|
usleep_range(100, 200);
|
|
}
|
|
|
|
static void rk628_combtxphy_lvds_power_on(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON7, SW_TX_MODE_MASK, SW_TX_MODE(3));
|
|
rk628_i2c_write(rk628, COMBTXPHY_CON10, TX7_CKDRV_EN | TX2_CKDRV_EN);
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_BUS_WIDTH_MASK |
|
|
SW_GVI_LVDS_EN_MASK | SW_MIPI_DSI_EN_MASK,
|
|
SW_BUS_WIDTH_7BIT | SW_GVI_LVDS_EN);
|
|
|
|
if (txphy->flags & COMBTXPHY_MODULEA_EN)
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_MODULEA_EN_MASK,
|
|
SW_MODULEA_EN);
|
|
if (txphy->flags & COMBTXPHY_MODULEB_EN)
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_MODULEB_EN_MASK,
|
|
SW_MODULEB_EN);
|
|
|
|
rk628_i2c_write(rk628, COMBTXPHY_CON5, SW_REF_DIV(txphy->ref_div - 1) |
|
|
SW_PLL_FB_DIV(txphy->fb_div) |
|
|
SW_PLL_FRAC_DIV(txphy->frac_div) |
|
|
SW_RATE(txphy->rate_div / 2));
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL | SW_TX_PD_MASK, 0);
|
|
usleep_range(100, 200);
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK, 0);
|
|
}
|
|
|
|
static void rk628_combtxphy_gvi_power_on(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
rk628_i2c_write(rk628, COMBTXPHY_CON5, SW_REF_DIV(txphy->ref_div - 1) |
|
|
SW_PLL_FB_DIV(txphy->fb_div) |
|
|
SW_PLL_FRAC_DIV(txphy->frac_div) |
|
|
SW_RATE(txphy->rate_div / 2));
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_BUS_WIDTH_MASK |
|
|
SW_GVI_LVDS_EN_MASK | SW_MIPI_DSI_EN_MASK |
|
|
SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK,
|
|
SW_BUS_WIDTH_10BIT | SW_GVI_LVDS_EN |
|
|
SW_MODULEB_EN | SW_MODULEA_EN);
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL | SW_TX_PD_MASK, 0);
|
|
usleep_range(100, 200);
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK, 0);
|
|
}
|
|
|
|
void rk628_txphy_set_mode(struct rk628 *rk628, enum phy_mode mode)
|
|
{
|
|
unsigned int fvco, frac_rate, fin = 24;
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
switch (mode) {
|
|
case PHY_MODE_VIDEO_MIPI:
|
|
{
|
|
int bus_width = rk628_txphy_get_bus_width(rk628);
|
|
unsigned int fhsc = bus_width >> 8;
|
|
unsigned int flags = bus_width & 0xff;
|
|
|
|
fhsc = fin * (fhsc / fin);
|
|
if (fhsc < 80 || fhsc > 2000)
|
|
return;
|
|
else if (fhsc < 375)
|
|
txphy->rate_div = 4;
|
|
else if (fhsc < 750)
|
|
txphy->rate_div = 2;
|
|
else
|
|
txphy->rate_div = 1;
|
|
|
|
txphy->flags = flags;
|
|
|
|
fvco = fhsc * 2 * txphy->rate_div;
|
|
txphy->ref_div = 1;
|
|
txphy->fb_div = fvco / 8 / fin;
|
|
frac_rate = fvco - (fin * 8 * txphy->fb_div);
|
|
if (frac_rate) {
|
|
frac_rate <<= 10;
|
|
frac_rate /= fin * 8;
|
|
txphy->frac_div = frac_rate;
|
|
} else {
|
|
txphy->frac_div = 0;
|
|
}
|
|
|
|
fvco = fin * (1024 * txphy->fb_div + txphy->frac_div);
|
|
fvco *= 8;
|
|
fvco = DIV_ROUND_UP(fvco, 1024 * txphy->ref_div);
|
|
fhsc = fvco / 2 / txphy->rate_div;
|
|
txphy->bus_width = fhsc;
|
|
|
|
break;
|
|
}
|
|
case PHY_MODE_VIDEO_LVDS:
|
|
{
|
|
int bus_width = rk628_txphy_get_bus_width(rk628);
|
|
unsigned int flags = bus_width & 0xff;
|
|
unsigned int rate = (bus_width >> 8) * 7;
|
|
|
|
txphy->flags = flags;
|
|
txphy->ref_div = 1;
|
|
txphy->fb_div = 14;
|
|
txphy->frac_div = 0;
|
|
|
|
if (rate < 500)
|
|
txphy->rate_div = 4;
|
|
else if (rate < 1000)
|
|
txphy->rate_div = 2;
|
|
else
|
|
txphy->rate_div = 1;
|
|
break;
|
|
}
|
|
case PHY_MODE_VIDEO_GVI:
|
|
{
|
|
unsigned int fhsc = rk628_txphy_get_bus_width(rk628) & 0xfff;
|
|
|
|
if (fhsc < 500 || fhsc > 4000)
|
|
return;
|
|
else if (fhsc < 1000)
|
|
txphy->rate_div = 4;
|
|
else if (fhsc < 2000)
|
|
txphy->rate_div = 2;
|
|
else
|
|
txphy->rate_div = 1;
|
|
fvco = fhsc * txphy->rate_div;
|
|
|
|
txphy->ref_div = 1;
|
|
txphy->fb_div = fvco / 8 / fin;
|
|
frac_rate = fvco - (fin * 8 * txphy->fb_div);
|
|
|
|
if (frac_rate) {
|
|
frac_rate <<= 10;
|
|
frac_rate /= fin * 8;
|
|
txphy->frac_div = frac_rate;
|
|
} else {
|
|
txphy->frac_div = 0;
|
|
}
|
|
|
|
fvco = fin * (1024 * txphy->fb_div + txphy->frac_div);
|
|
fvco *= 8;
|
|
fvco /= 1024 * txphy->ref_div;
|
|
fhsc = fvco / txphy->rate_div;
|
|
txphy->bus_width = fhsc;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
txphy->mode = mode;
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_set_mode);
|
|
|
|
void rk628_txphy_power_on(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy = rk628->txphy;
|
|
|
|
rk628_control_assert(rk628, RGU_TXPHY_CON);
|
|
udelay(10);
|
|
rk628_control_deassert(rk628, RGU_TXPHY_CON);
|
|
udelay(10);
|
|
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK | SW_TX_PD_MASK |
|
|
SW_PD_PLL_MASK, SW_TX_IDLE(0x3ff) |
|
|
SW_TX_PD(0x3ff) | SW_PD_PLL);
|
|
|
|
switch (txphy->mode) {
|
|
case PHY_MODE_VIDEO_MIPI:
|
|
|
|
rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
|
|
SW_TXPHY_REFCLK_SEL_MASK,
|
|
SW_TXPHY_REFCLK_SEL(0));
|
|
rk628_combtxphy_dsi_power_on(rk628);
|
|
break;
|
|
case PHY_MODE_VIDEO_LVDS:
|
|
rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
|
|
SW_TXPHY_REFCLK_SEL_MASK,
|
|
SW_TXPHY_REFCLK_SEL(1));
|
|
rk628_combtxphy_lvds_power_on(rk628);
|
|
break;
|
|
case PHY_MODE_VIDEO_GVI:
|
|
rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
|
|
SW_TXPHY_REFCLK_SEL_MASK,
|
|
SW_TXPHY_REFCLK_SEL(0));
|
|
rk628_combtxphy_gvi_power_on(rk628);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_power_on);
|
|
|
|
void rk628_txphy_power_off(struct rk628 *rk628)
|
|
{
|
|
rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK | SW_TX_PD_MASK |
|
|
SW_PD_PLL_MASK | SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK |
|
|
SW_GVI_LVDS_EN_MASK | SW_MIPI_DSI_EN_MASK,
|
|
SW_TX_IDLE(0x3ff) | SW_TX_PD(0x3ff) | SW_PD_PLL);
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_power_off);
|
|
|
|
struct rk628_combtxphy *rk628_txphy_register(struct rk628 *rk628)
|
|
{
|
|
struct rk628_combtxphy *txphy;
|
|
|
|
txphy = devm_kzalloc(rk628->dev, sizeof(*txphy), GFP_KERNEL);
|
|
if (!txphy)
|
|
return NULL;
|
|
|
|
rk628->txphy = txphy;
|
|
|
|
return txphy;
|
|
}
|
|
EXPORT_SYMBOL(rk628_txphy_register);
|