#define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ #define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ #define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of Gen3 but E3) */ #define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ #define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */
if (freq < 42000000)
val = LVDPLLCR_PLLDIVCNT_42M; elseif (freq < 85000000)
val = LVDPLLCR_PLLDIVCNT_85M; elseif (freq < 128000000)
val = LVDPLLCR_PLLDIVCNT_128M; else
val = LVDPLLCR_PLLDIVCNT_148M;
/* * The LVDS PLL is made of a pre-divider and a multiplier (strangely * enough called M and N respectively), followed by a post-divider E. * * ,-----. ,-----. ,-----. ,-----. * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout * `-----' ,-> | | `-----' | `-----' * | `-----' | * | ,-----. | * `-------- | 1/N | <-------' * `-----' * * The clock output by the PLL is then further divided by a programmable * divider DIV to achieve the desired target frequency. Finally, an * optional fixed /7 divider is used to convert the bit clock to a pixel * clock (as LVDS transmits 7 bits per lane per clock sample). * * ,-------. ,-----. |\ * Fout --> | 1/DIV | --> | 1/7 | --> | | * `-------' | `-----' | | --> dot clock * `------------> | | * |/ * * The /7 divider is optional, it is enabled when the LVDS PLL is used * to drive the LVDS encoder, and disabled when used to generate a dot * clock for the DU RGB output, without using the LVDS encoder. * * The PLL allowed input frequency range is 12 MHz to 192 MHz.
*/
fin = clk_get_rate(clk); if (fin < 12000000 || fin > 192000000) return;
/* * The comparison frequency range is 12 MHz to 24 MHz, which limits the * allowed values for the pre-divider M (normal range 1-8). * * Fpfd = Fin / M
*/
m_min = max_t(unsignedint, 1, DIV_ROUND_UP(fin, 24000000));
m_max = min_t(unsignedint, 8, fin / 12000000);
for (m = m_min; m <= m_max; ++m) { unsignedlong fpfd; unsignedint n_min; unsignedint n_max; unsignedint n;
/* * The VCO operating range is 900 Mhz to 1800 MHz, which limits * the allowed values for the multiplier N (normal range * 60-120). * * Fvco = Fin * N / M
*/
fpfd = fin / m;
n_min = max_t(unsignedint, 60, DIV_ROUND_UP(900000000, fpfd));
n_max = min_t(unsignedint, 120, 1800000000 / fpfd);
for (n = n_min; n < n_max; ++n) { unsignedlong fvco; unsignedint e_min; unsignedint e;
/* * The output frequency is limited to 1039.5 MHz, * limiting again the allowed values for the * post-divider E (normal value 1, 2 or 4). * * Fout = Fvco / E
*/
fvco = fpfd * n;
e_min = fvco > 1039500000 ? 1 : 0;
for (e = e_min; e < 3; ++e) { unsignedlong fout; unsignedlong diff; unsignedint div;
/* * Finally we have a programable divider after * the PLL, followed by a an optional fixed /7 * divider.
*/
fout = fvco / (1 << e) / div7;
div = max(1UL, DIV_ROUND_CLOSEST(fout, target));
diff = abs(fout / div - target);
if (pll.div > 1) /* * The DIVRESET bit is a misnomer, setting it to 1 deasserts the * divisor reset.
*/
rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL |
LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1)); else
rcar_lvds_write(lvds, LVDDIV, 0);
}
/* * There is no API yet to retrieve LVDS mode from a bridge, only panels * are supported.
*/ if (!lvds->panel) return RCAR_LVDS_MODE_JEIDA;
info = &connector->display_info; if (!info->num_bus_formats || !info->bus_formats) {
dev_warn(lvds->dev, "no LVDS bus format reported, using JEIDA\n"); return RCAR_LVDS_MODE_JEIDA;
}
switch (info->bus_formats[0]) { case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
mode = RCAR_LVDS_MODE_JEIDA; break; case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
mode = RCAR_LVDS_MODE_VESA; break; default:
dev_warn(lvds->dev, "unsupported LVDS bus format 0x%04x, using JEIDA\n",
info->bus_formats[0]); return RCAR_LVDS_MODE_JEIDA;
}
if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
mode |= RCAR_LVDS_MODE_MIRROR;
if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) {
u32 lvdstripe = 0;
if (lvds->link_type != RCAR_LVDS_SINGLE_LINK) { /* * By default we generate even pixels from the primary * encoder and odd pixels from the companion encoder. * Swap pixels around if the sink requires odd pixels * from the primary encoder and even pixels from the * companion encoder.
*/ bool swap_pixels = lvds->link_type ==
RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
/* * Configure vertical stripe since we are dealing with * an LVDS dual-link connection. * * ST_SWAP is reserved for the companion encoder, only * set it in the primary encoder.
*/
lvdstripe = LVDSTRIPE_ST_ON
| (lvds->companion && swap_pixels ?
LVDSTRIPE_ST_SWAP : 0);
}
rcar_lvds_write(lvds, LVDSTRIPE, lvdstripe);
}
/* * PLL clock configuration on all instances but the companion in * dual-link mode. * * The extended PLL has been turned on by an explicit call to * rcar_lvds_pclk_enable() from the DU driver.
*/ if ((lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) &&
!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { conststruct drm_crtc_state *crtc_state =
drm_atomic_get_new_crtc_state(state, crtc); conststruct drm_display_mode *mode =
&crtc_state->adjusted_mode;
/* Set the LVDS mode and select the input. */
lvdcr0 = rcar_lvds_get_lvds_mode(lvds, connector) << LVDCR0_LVMD_SHIFT;
if (lvds->bridge.encoder) { if (drm_crtc_index(crtc) == 2)
lvdcr0 |= LVDCR0_DUSEL;
}
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
/* Turn all the channels on. */
rcar_lvds_write(lvds, LVDCR1,
LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
if (lvds->info->gen < 3) { /* Enable LVDS operation and turn the bias circuitry on. */
lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}
if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { /* * Turn the PLL on (simple PLL only, extended PLL is fully * controlled through LVDPLLCR).
*/
lvdcr0 |= LVDCR0_PLLON;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}
if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) { /* Set LVDS normal mode. */
lvdcr0 |= LVDCR0_PWD;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}
if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) { /* * Turn on the LVDS PHY. On D3, the LVEN and LVRES bit must be * set at the same time, so don't write the register yet.
*/
lvdcr0 |= LVDCR0_LVEN; if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_PWD))
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}
if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { /* Wait for the PLL startup delay (simple PLL only). */
usleep_range(100, 150);
}
/* Turn the output on. */
lvdcr0 |= LVDCR0_LVRES;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
}
/* * Clear the LVDCR0 bits in the order specified by the hardware * documentation, ending with a write of 0 to the full register to * clear all remaining bits.
*/
lvdcr0 = rcar_lvds_read(lvds, LVDCR0);
/* The extended PLL is turned off in rcar_lvds_pclk_disable(). */ if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
rcar_lvds_write(lvds, LVDPLLCR, 0);
/* Disable the companion LVDS encoder in dual-link mode. */ if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
rcar_lvds_disable(lvds->companion);
pm_runtime_put_sync(lvds->dev);
}
/* ----------------------------------------------------------------------------- * Clock - D3/E3 only
*/
int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsignedlong freq, bool dot_clk_only)
{ struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); int ret;
if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) return -ENODEV;
/* * For D3 and E3, disabling the LVDS encoder before the DU would stall * the DU, causing a vblank wait timeout when stopping the DU. This has * been traced to clearing the LVEN bit, but the exact reason is * unknown. Keep the encoder enabled, it will be disabled by an explicit * call to rcar_lvds_pclk_disable() from the DU driver. * * We could clear the LVRES bit already to disable the LVDS output, but * that's likely pointless.
*/ if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) return;
/* * The internal LVDS encoder has a restricted clock frequency operating * range, from 5MHz to 148.5MHz on D3 and E3, and from 31MHz to * 148.5MHz on all other platforms. Clamp the clock accordingly.
*/
min_freq = lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL ? 5000 : 31000;
adjusted_mode->clock = clamp(adjusted_mode->clock, min_freq, 148500);
staticint rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds)
{ conststruct of_device_id *match; struct device_node *companion; struct device_node *port0, *port1; struct rcar_lvds *companion_lvds; struct device *dev = lvds->dev; int dual_link; int ret = 0;
/* Locate the companion LVDS encoder for dual-link operation, if any. */
companion = of_parse_phandle(dev->of_node, "renesas,companion", 0); if (!companion) return 0;
/* * Sanity check: the companion encoder must have the same compatible * string.
*/
match = of_match_device(dev->driver->of_match_table, dev); if (!of_device_is_compatible(companion, match->compatible)) {
dev_err(dev, "Companion LVDS encoder is invalid\n");
ret = -ENXIO; goto done;
}
/* * We need to work out if the sink is expecting us to function in * dual-link mode. We do this by looking at the DT port nodes we are * connected to, if they are marked as expecting even pixels and * odd pixels than we need to enable vertical stripe output.
*/
port0 = of_graph_get_port_by_id(dev->of_node, 1);
port1 = of_graph_get_port_by_id(companion, 1);
dual_link = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
of_node_put(port0);
of_node_put(port1);
switch (dual_link) { case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
lvds->link_type = RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; break; case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; break; default: /* * Early dual-link bridge specific implementations populate the * timings field of drm_bridge. If the flag is set, we assume * that we are expected to generate even pixels from the primary * encoder, and odd pixels from the companion encoder.
*/ if (lvds->next_bridge->timings &&
lvds->next_bridge->timings->dual_link)
lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; else
lvds->link_type = RCAR_LVDS_SINGLE_LINK;
}
if (lvds->link_type == RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
dev_dbg(dev, "Data swapping required\n");
/* * FIXME: We should not be messing with the companion encoder private * data from the primary encoder, we should rather let the companion * encoder work things out on its own. However, the companion encoder * doesn't hold a reference to the primary encoder, and * drm_of_lvds_get_dual_link_pixel_order needs to be given references * to the output ports of both encoders, therefore leave it like this * for the time being.
*/
companion_lvds = bridge_to_rcar_lvds(lvds->companion);
companion_lvds->link_type = lvds->link_type;
done:
of_node_put(companion);
return ret;
}
staticint rcar_lvds_parse_dt(struct rcar_lvds *lvds)
{ int ret;
ret = drm_of_find_panel_or_bridge(lvds->dev->of_node, 1, 0,
&lvds->panel, &lvds->next_bridge); if (ret) goto done;
if (lvds->panel) {
lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
lvds->panel); if (IS_ERR_OR_NULL(lvds->next_bridge)) {
ret = -EINVAL; goto done;
}
}
if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK)
ret = rcar_lvds_parse_dt_companion(lvds);
done: /* * On D3/E3 the LVDS encoder provides a clock to the DU, which can be * used for the DPAD output even when the LVDS output is not connected. * Don't fail probe in that case as the DU will need the bridge to * control the clock.
*/ if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) return ret == -ENODEV ? 0 : ret;
/* * LVDS encoders without an extended PLL have no external clock inputs.
*/ if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) return 0;
lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true); if (IS_ERR(lvds->clocks.extal)) return PTR_ERR(lvds->clocks.extal);
lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true); if (IS_ERR(lvds->clocks.dotclkin[0])) return PTR_ERR(lvds->clocks.dotclkin[0]);
lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true); if (IS_ERR(lvds->clocks.dotclkin[1])) return PTR_ERR(lvds->clocks.dotclkin[1]);
/* At least one input to the PLL must be available. */ if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] &&
!lvds->clocks.dotclkin[1]) {
dev_err(lvds->dev, "no input clock (extal, dclkin.0 or dclkin.1)\n"); return -EINVAL;
}
attr = soc_device_match(lvds_quirk_matches); if (attr)
lvds->info = attr->data;
ret = rcar_lvds_parse_dt(lvds); if (ret < 0) return ret;
lvds->bridge.of_node = pdev->dev.of_node;
lvds->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lvds->mmio)) return PTR_ERR(lvds->mmio);
ret = rcar_lvds_get_clocks(lvds); if (ret < 0) return ret;
lvds->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(lvds->rstc)) return dev_err_probe(&pdev->dev, PTR_ERR(lvds->rstc), "failed to get cpg reset\n");
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.