/* * Note these registers do not appear in the datasheet, they are * however present in the BSP driver which is where these values * come from. Name GRF_VO_CON3 is assumed.
*/ #define RK3568_GRF_VO_CON3 0x36c #define RK3568_DSI1_SKEWCALHS (0x1f << 11) #define RK3568_DSI1_FORCETXSTOPMODE (0xf << 4) #define RK3568_DSI1_TURNDISABLE BIT(2) #define RK3568_DSI1_FORCERXMODE BIT(0)
/* being a phy for other mipi hosts */ unsignedint usage_mode; struct mutex usage_mutex; struct phy *dphy; struct phy_configure_opts_mipi_dphy dphy_config;
unsignedint lane_mbps; /* per lane */
u16 input_div;
u16 feedback_div;
u32 format;
staticvoid dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi,
u8 test_code,
u8 test_data)
{ /* * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content * is latched internally as the current test code. Test data is * programmed internally by rising edge on TESTCLK.
*/
dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
/* * ns2ui - Nanoseconds to UI time periods
*/ staticinlineunsignedint ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
{ return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
}
staticint dw_mipi_dsi_phy_init(void *priv_data)
{ struct dw_mipi_dsi_rockchip *dsi = priv_data; int ret, i, vco;
if (dsi->phy) return 0;
/* * Get vco from frequency(lane_mbps) * vco frequency table * 000 - between 80 and 200 MHz * 001 - between 200 and 300 MHz * 010 - between 300 and 500 MHz * 011 - between 500 and 700 MHz * 100 - between 700 and 900 MHz * 101 - between 900 and 1100 MHz * 110 - between 1100 and 1300 MHz * 111 - between 1300 and 1500 MHz
*/
vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
i = max_mbps_to_parameter(dsi->lane_mbps); if (i < 0) {
DRM_DEV_ERROR(dsi->dev, "failed to get parameter for %dmbps clock\n",
dsi->lane_mbps); return i;
}
ret = clk_prepare_enable(dsi->phy_cfg_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n"); return ret;
}
dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
INPUT_DIVIDER(dsi->input_div));
dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
LOOP_DIV_LOW_SEL(dsi->feedback_div) |
LOW_PROGRAM_EN); /* * We need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately * to make the configured LSB effective according to IP simulation * and lab test results. * Only in this way can we get correct mipi phy pll frequency.
*/
dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
HIGH_PROGRAM_EN);
dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
dsi->format = format;
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); if (bpp < 0) {
DRM_DEV_ERROR(dsi->dev, "failed to get bpp for pixel format %d\n",
dsi->format); return bpp;
}
mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC); if (mpclk) { /* take 1 / 0.8, since mbps must big than bandwidth of RGB */
tmp = mpclk * (bpp / lanes) * 10 / 8; if (tmp < max_mbps)
target_mbps = tmp; else
DRM_DEV_ERROR(dsi->dev, "DPHY clock frequency is out of range\n");
}
/* for external phy only a the mipi_dphy_config is necessary */ if (dsi->phy) {
phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8,
bpp, lanes,
&dsi->phy_opts.mipi_dphy);
dsi->lane_mbps = target_mbps;
*lane_mbps = dsi->lane_mbps;
return 0;
}
fin = clk_get_rate(dsi->pllref_clk);
fout = target_mbps * USEC_PER_SEC;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta; /* Fvco = Fref * M / N */
tmp = (u64)fout * _prediv;
do_div(tmp, fin);
_fbdiv = tmp; /* * Due to the use of a "by 2 pre-scaler," the range of the * feedback multiplication value M is limited to even division * numbers, and m must be greater than 6, not bigger than 512.
*/ if (_fbdiv < 6 || _fbdiv > 512) continue;
mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
&dsi->encoder.encoder); if (mux < 0) return;
/* * For the RK3399, the clk of grf must be enabled before writing grf * register. And for RK3288 or other soc, this grf_clk must be NULL, * the clk_prepare_enable return true directly.
*/
ret = clk_prepare_enable(dsi->grf_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); return;
}
dw_mipi_dsi_rockchip_set_lcdsel(dsi, mux); if (dsi->slave)
dw_mipi_dsi_rockchip_set_lcdsel(dsi->slave, mux);
match = of_match_device(dsi->dev->driver->of_match_table, dsi->dev);
local = of_graph_get_remote_node(dsi->dev->of_node, 1, 0); if (!local) return NULL;
while ((node = of_find_compatible_node(node, NULL,
match->compatible))) { struct device_node *remote;
/* found ourself */ if (node == dsi->dev->of_node) continue;
remote = of_graph_get_remote_node(node, 1, 0); if (!remote) continue;
/* same display device in port1-ep0 for both */ if (remote == local) { struct dw_mipi_dsi_rockchip *dsi2; struct platform_device *pdev;
pdev = of_find_device_by_node(node);
/* * we have found the second, so will either return it * or return with an error. In any case won't need the * nodes anymore nor continue the loop.
*/
of_node_put(remote);
of_node_put(node);
of_node_put(local);
if (!pdev) return ERR_PTR(-EPROBE_DEFER);
dsi2 = platform_get_drvdata(pdev); if (!dsi2) {
platform_device_put(pdev); return ERR_PTR(-EPROBE_DEFER);
}
pm_runtime_get_sync(dsi->dev); if (dsi->slave)
pm_runtime_get_sync(dsi->slave->dev);
ret = clk_prepare_enable(dsi->pllref_clk); if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret); goto out_pm_runtime;
}
/* * With the GRF clock running, write lane and dual-mode configurations * that won't change immediately. If we waited until enable() to do * this, things like panel preparation would not be able to send * commands over DSI.
*/
ret = clk_prepare_enable(dsi->grf_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); goto out_pll_clk;
}
dw_mipi_dsi_rockchip_config(dsi); if (dsi->slave)
dw_mipi_dsi_rockchip_config(dsi->slave);
clk_disable_unprepare(dsi->grf_clk);
ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev); if (ret) {
DRM_DEV_ERROR(dev, "Failed to create drm encoder\n"); goto out_pll_clk;
}
rockchip_drm_encoder_set_crtc_endpoint_id(&dsi->encoder,
dev->of_node, 0, 0);
ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder); if (ret) {
DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret); goto out_pll_clk;
}
dsi->dsi_bound = true;
return 0;
out_pll_clk:
clk_disable_unprepare(dsi->pllref_clk);
out_pm_runtime:
pm_runtime_put(dsi->dev); if (dsi->slave)
pm_runtime_put(dsi->slave->dev);
ret = component_add(dsi->dev, &dw_mipi_dsi_rockchip_ops); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to register component: %d\n",
ret); goto out;
}
second = dw_mipi_dsi_rockchip_find_second(dsi); if (IS_ERR(second)) {
ret = PTR_ERR(second); goto out;
} if (second) {
ret = component_add(second, &dw_mipi_dsi_rockchip_ops); if (ret) {
DRM_DEV_ERROR(second, "Failed to register component: %d\n",
ret); goto out;
}
}
staticint dw_mipi_dsi_rockchip_dphy_bind(struct device *dev, struct device *master, void *data)
{ /* * Nothing to do when used as a dphy. * Just make the rest of Rockchip-DRM happy * by being here.
*/
return 0;
}
staticvoid dw_mipi_dsi_rockchip_dphy_unbind(struct device *dev, struct device *master, void *data)
{ /* Nothing to do when used as a dphy. */
}
i = max_mbps_to_parameter(dsi->lane_mbps); if (i < 0) {
DRM_DEV_ERROR(dsi->dev, "failed to get parameter for %dmbps clock\n",
dsi->lane_mbps); return i;
}
ret = pm_runtime_resume_and_get(dsi->dev); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "failed to enable device: %d\n", ret); return ret;
}
ret = clk_prepare_enable(dsi->pclk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk: %d\n", ret); goto err_pclk;
}
ret = clk_prepare_enable(dsi->grf_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); goto err_grf_clk;
}
ret = clk_prepare_enable(dsi->phy_cfg_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk: %d\n", ret); goto err_phy_cfg_clk;
}
/* do soc-variant specific init */ if (dsi->cdata->dphy_rx_power_on) {
ret = dsi->cdata->dphy_rx_power_on(phy); if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "hardware-specific phy bringup failed: %d\n", ret); goto err_pwr_on;
}
}
/* * Configure hsfreqrange according to frequency values * Set clock lane and hsfreqrange by lane0(test code 0x44)
*/
dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_CLK, 0);
dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_1, 0);
dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_2, 0);
dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_3, 0);
/* Normal operation */
dw_mipi_dsi_phy_write(dsi, 0x0, 0);
/* * Re-configure DSI state, if we were previously initialized. We need * to do this before rockchip_drm_drv tries to re-enable() any panels.
*/ if (dsi->dsi_bound) {
ret = clk_prepare_enable(dsi->grf_clk); if (ret) {
DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); return ret;
}
dw_mipi_dsi_rockchip_config(dsi); if (dsi->slave)
dw_mipi_dsi_rockchip_config(dsi->slave);
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); if (!dsi) return -ENOMEM;
dsi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(dsi->base)) {
DRM_DEV_ERROR(dev, "Unable to get dsi registers\n"); return PTR_ERR(dsi->base);
}
i = 0; while (cdata[i].reg) { if (cdata[i].reg == res->start) {
dsi->cdata = &cdata[i]; break;
}
i++;
}
if (!dsi->cdata) {
DRM_DEV_ERROR(dev, "no dsi-config for %s node\n", np->name); return -EINVAL;
}
/* try to get a possible external dphy */
dsi->phy = devm_phy_optional_get(dev, "dphy"); if (IS_ERR(dsi->phy)) {
ret = PTR_ERR(dsi->phy);
DRM_DEV_ERROR(dev, "failed to get mipi dphy: %d\n", ret); return ret;
}
dsi->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret); return ret;
}
dsi->pllref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(dsi->pllref_clk)) { if (dsi->phy) { /* * if external phy is present, pll will be * generated there.
*/
dsi->pllref_clk = NULL;
} else {
ret = PTR_ERR(dsi->pllref_clk);
DRM_DEV_ERROR(dev, "Unable to get pll reference clock: %d\n",
ret); return ret;
}
}
if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg"); if (IS_ERR(dsi->phy_cfg_clk)) {
ret = PTR_ERR(dsi->phy_cfg_clk);
DRM_DEV_ERROR(dev, "Unable to get phy_cfg_clk: %d\n", ret); return ret;
}
}
if (dsi->cdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
dsi->grf_clk = devm_clk_get(dev, "grf"); if (IS_ERR(dsi->grf_clk)) {
ret = PTR_ERR(dsi->grf_clk);
DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret); return ret;
}
}
dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(dsi->grf_regmap)) {
DRM_DEV_ERROR(dev, "Unable to get rockchip,grf\n"); return PTR_ERR(dsi->grf_regmap);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.