/* dsi color format coding according to the datasheet */ enum dsi_color {
DSI_RGB565_CONF1,
DSI_RGB565_CONF2,
DSI_RGB565_CONF3,
DSI_RGB666_CONF1,
DSI_RGB666_CONF2,
DSI_RGB888,
};
staticenum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
{ switch (fmt) { case MIPI_DSI_FMT_RGB888: return DSI_RGB888; case MIPI_DSI_FMT_RGB666: return DSI_RGB666_CONF2; case MIPI_DSI_FMT_RGB666_PACKED: return DSI_RGB666_CONF1; case MIPI_DSI_FMT_RGB565: return DSI_RGB565_CONF1; default:
DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n");
} return DSI_RGB888;
}
staticint dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
{ int divisor = idf * odf;
/* prevent from division by 0 */ if (!divisor) return 0;
staticint dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi, int clkin_khz, int clkout_khz, int *idf, int *ndiv, int *odf)
{ int i, o, n, n_min, n_max; int fvco_min, fvco_max, delta, best_delta; /* all in khz */
/* Early checks preventing division by 0 & odd results */ if (clkin_khz <= 0 || clkout_khz <= 0) return -EINVAL;
best_delta = 1000000; /* big started value (1000000khz) */
for (i = IDF_MIN; i <= IDF_MAX; i++) { /* Compute ndiv range according to Fvco */
n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
n_max = (fvco_max * i) / (2 * clkin_khz);
/* No need to continue idf loop if we reach ndiv max */ if (n_min >= NDIV_MAX) break;
/* Clamp ndiv to valid values */ if (n_min < NDIV_MIN)
n_min = NDIV_MIN; if (n_max > NDIV_MAX)
n_max = NDIV_MAX;
for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz); /* Check ndiv according to vco range */ if (n < n_min || n > n_max) continue; /* Check if new delta is better & saves parameters */
delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
clkout_khz; if (delta < 0)
delta = -delta; if (delta < best_delta) {
*idf = i;
*ndiv = n;
*odf = o;
best_delta = delta;
} /* fast return in case of "perfect result" */ if (!delta) return 0;
}
}
/* Add 20% to pll out to be higher than pixel bw (burst mode only) */ if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
pll_out_khz = (pll_out_khz * 12) / 10;
if (pll_out_khz > dsi->lane_max_kbps) {
pll_out_khz = dsi->lane_max_kbps;
DRM_WARN("Warning max phy mbps is used\n");
} if (pll_out_khz < dsi->lane_min_kbps) {
pll_out_khz = dsi->lane_min_kbps;
DRM_WARN("Warning min phy mbps is used\n");
}
ret = clk_set_rate((dsi->txbyte_clk.clk), pll_out_khz * 1000); if (ret)
DRM_DEBUG_DRIVER("ERROR Could not set rate of %d to %s clk->name",
pll_out_khz, clk_hw_get_name(&dsi->txbyte_clk));
/* Select video mode by resetting DSIM bit */
dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
/* Select the color coding */
dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
dsi_color_from_mipi(format) << 1);
if (pll_out_khz > dsi->lane_max_kbps) return MODE_CLOCK_HIGH;
if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { /* Add 20% to pll out to be higher than pixel bw */
pll_out_khz = (pll_out_khz * 12) / 10;
} else { if (pll_out_khz < dsi->lane_min_kbps) return MODE_CLOCK_LOW;
}
/* Compute best pll parameters */
idf = 0;
ndiv = 0;
odf = 0;
pll_in_khz = clk_get_rate(dsi->pllref_clk) / 1000;
ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, &idf, &ndiv, &odf); if (ret) {
DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); return MODE_ERROR;
}
if (!(mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) { unsignedint px_clock_hz, target_px_clock_hz, lane_mbps; int dsi_short_packet_size_px, hfp, hsync, hbp, delay_to_lp; struct dw_mipi_dsi_dphy_timing dphy_timing;
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
px_clock_hz = DIV_ROUND_CLOSEST_ULL(1000ULL * pll_out_khz * lanes, bpp);
target_px_clock_hz = mode->clock * 1000; /* * Filter modes according to the clock value, particularly useful for * hdmi modes that require precise pixel clocks.
*/ if (px_clock_hz < target_px_clock_hz - CLK_TOLERANCE_HZ ||
px_clock_hz > target_px_clock_hz + CLK_TOLERANCE_HZ) return MODE_CLOCK_RANGE;
/* sync packets are codes as DSI short packets (4 bytes) */
dsi_short_packet_size_px = DIV_ROUND_UP(4 * BITS_PER_BYTE, bpp);
/* hsync must be longer than 4 bytes HSS packets */ if (hsync < dsi_short_packet_size_px) return MODE_HSYNC_NARROW;
if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { /* HBP must be longer than 4 bytes HSE packets */ if (hbp < dsi_short_packet_size_px) return MODE_HSYNC_NARROW;
hbp -= dsi_short_packet_size_px;
} else { /* With sync events HBP extends in the hsync */
hbp += hsync - dsi_short_packet_size_px;
}
lane_mbps = pll_out_khz / 1000;
ret = dw_mipi_dsi_phy_get_timing(priv_data, lane_mbps, &dphy_timing); if (ret) return MODE_ERROR; /* * In non-burst mode DSI has to enter in LP during HFP * (horizontal front porch) or HBP (horizontal back porch) to * resync with LTDC pixel clock.
*/
delay_to_lp = DIV_ROUND_UP((dphy_timing.data_hs2lp + dphy_timing.data_lp2hs) *
lanes * BITS_PER_BYTE, bpp); if (hfp < delay_to_lp && hbp < delay_to_lp) return MODE_HSYNC;
}
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); if (!dsi) return -ENOMEM;
dsi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dsi->base)) {
ret = PTR_ERR(dsi->base);
DRM_ERROR("Unable to get dsi registers %d\n", ret); return ret;
}
dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi"); if (IS_ERR(dsi->vdd_supply)) {
ret = PTR_ERR(dsi->vdd_supply);
dev_err_probe(dev, ret, "Failed to request regulator\n"); return ret;
}
ret = regulator_enable(dsi->vdd_supply); if (ret) {
DRM_ERROR("Failed to enable regulator: %d\n", ret); return ret;
}
dsi->pllref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(dsi->pllref_clk)) {
ret = PTR_ERR(dsi->pllref_clk);
dev_err_probe(dev, ret, "Unable to get pll reference clock\n"); goto err_clk_get;
}
ret = clk_prepare_enable(dsi->pllref_clk); if (ret) {
DRM_ERROR("Failed to enable pllref_clk: %d\n", ret); goto err_clk_get;
}
dsi->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_ERROR("Unable to get peripheral clock: %d\n", ret); goto err_dsi_probe;
}
ret = clk_prepare_enable(dsi->pclk); if (ret) {
DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); goto err_dsi_probe;
}
if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
ret = -ENODEV;
DRM_ERROR("bad dsi hardware version\n"); goto err_dsi_probe;
}
/* set lane capabilities according to hw version */
dsi->lane_min_kbps = LANE_MIN_KBPS;
dsi->lane_max_kbps = LANE_MAX_KBPS; if (dsi->hw_version == HWVER_131) {
dsi->lane_min_kbps *= 2;
dsi->lane_max_kbps *= 2;
}
dsi->dsi = dw_mipi_dsi_probe(pdev, &dsi->pdata); if (IS_ERR(dsi->dsi)) {
ret = PTR_ERR(dsi->dsi);
dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n"); goto err_dsi_probe;
}
/* * We need to wait for the generic bridge to probe before enabling and * register the internal pixel clock.
*/
ret = clk_prepare_enable(dsi->pclk); if (ret) {
DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); goto err_dsi_probe;
}
ret = dw_mipi_dsi_clk_register(dsi, dev); if (ret) {
DRM_ERROR("Failed to register DSI pixel clock: %d\n", ret);
clk_disable_unprepare(dsi->pclk); goto err_dsi_probe;
}
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.