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);