static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx, conststruct drm_display_mode *mode)
{ /* * The encoding of the LVDS_CLK_RANGE is as follows: * 000 - 25 MHz <= LVDS_CLK < 37.5 MHz * 001 - 37.5 MHz <= LVDS_CLK < 62.5 MHz * 010 - 62.5 MHz <= LVDS_CLK < 87.5 MHz * 011 - 87.5 MHz <= LVDS_CLK < 112.5 MHz * 100 - 112.5 MHz <= LVDS_CLK < 137.5 MHz * 101 - 137.5 MHz <= LVDS_CLK <= 154 MHz * which is a range of 12.5MHz..162.5MHz in 50MHz steps, except that * the ends of the ranges are clamped to the supported range. Since * sn65dsi83_mode_valid() already filters the valid modes and limits * the clock to 25..154 MHz, the range calculation can be simplified * as follows:
*/ int mode_clock = mode->clock;
if (ctx->lvds_dual_link)
mode_clock /= 2;
return (mode_clock - 12500) / 25000;
}
static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx, conststruct drm_display_mode *mode)
{ /* * The encoding of the CHA_DSI_CLK_RANGE is as follows: * 0x00 through 0x07 - Reserved * 0x08 - 40 <= DSI_CLK < 45 MHz * 0x09 - 45 <= DSI_CLK < 50 MHz * ... * 0x63 - 495 <= DSI_CLK < 500 MHz * 0x64 - 500 MHz * 0x65 through 0xFF - Reserved * which is DSI clock in 5 MHz steps, clamped to 40..500 MHz. * The DSI clock are calculated as: * DSI_CLK = mode clock * bpp / dsi_data_lanes / 2 * the 2 is there because the bus is DDR.
*/ return DIV_ROUND_UP(clamp((unsignedint)mode->clock *
mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
ctx->dsi->lanes / 2, 40000U, 500000U), 5000U);
}
static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
{ /* The divider is (DSI_CLK / LVDS_CLK) - 1, which really is: */ unsignedint dsi_div = mipi_dsi_pixel_format_to_bpp(ctx->dsi->format);
dsi_div /= ctx->dsi->lanes;
if (!ctx->lvds_dual_link)
dsi_div /= 2;
return dsi_div - 1;
}
staticint sn65dsi83_reset_pipe(struct sn65dsi83 *sn65dsi83)
{ struct drm_modeset_acquire_ctx ctx; int err;
/* * Reset active outputs of the related CRTC. * * This way, drm core will reconfigure each components in the CRTC * outputs path. In our case, this will force the previous component to * go back in LP11 mode and so allow the reconfiguration of SN65DSI83 * bridge. * * Keep the lock during the whole operation to be atomic.
*/
/* Reset the pipe */
ret = sn65dsi83_reset_pipe(ctx); if (ret) {
dev_err(ctx->dev, "reset pipe failed %pe\n", ERR_PTR(ret)); return;
} if (ctx->irq)
enable_irq(ctx->irq);
}
staticvoid sn65dsi83_handle_errors(struct sn65dsi83 *ctx)
{ unsignedint irq_stat; int ret;
/* * Schedule a reset in case of: * - the bridge doesn't answer * - the bridge signals an error
*/
ret = regmap_read(ctx->regmap, REG_IRQ_STAT, &irq_stat); if (ret || irq_stat) { /* * IRQ acknowledged is not always possible (the bridge can be in * a state where it doesn't answer anymore). To prevent an * interrupt storm, disable interrupt. The interrupt will be * after the reset.
*/ if (ctx->irq)
disable_irq_nosync(ctx->irq);
/* Get the LVDS format from the bridge state. */
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
switch (bridge_state->output_bus_cfg.format) { case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
lvds_format_24bpp = false;
lvds_format_jeida = true; break; case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
lvds_format_24bpp = true;
lvds_format_jeida = true; break; case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
lvds_format_24bpp = true;
lvds_format_jeida = false; break; default: /* * Some bridges still don't set the correct * LVDS bus pixel format, use SPWG24 default * format until those are fixed.
*/
lvds_format_24bpp = true;
lvds_format_jeida = false;
dev_warn(ctx->dev, "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
bridge_state->output_bus_cfg.format); break;
}
/* * Retrieve the CRTC adjusted mode. This requires a little dance to go * from the bridge to the encoder, to the connector and to the CRTC.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
mode = &crtc_state->adjusted_mode;
/* Set number of DSI lanes and LVDS link config. */
regmap_write(ctx->regmap, REG_DSI_LANE,
REG_DSI_LANE_DSI_CHANNEL_MODE_SINGLE |
REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi->lanes - 1)) | /* CHB is DSI85-only, set to default on DSI83/DSI84 */
REG_DSI_LANE_CHB_DSI_LANES(3)); /* No equalization. */
regmap_write(ctx->regmap, REG_DSI_EQ, 0x00);
/* Set up sync signal polarity. */
val = (mode->flags & DRM_MODE_FLAG_NHSYNC ?
REG_LVDS_FMT_HS_NEG_POLARITY : 0) |
(mode->flags & DRM_MODE_FLAG_NVSYNC ?
REG_LVDS_FMT_VS_NEG_POLARITY : 0);
val |= bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_LOW ?
REG_LVDS_FMT_DE_NEG_POLARITY : 0;
/* Set up bits-per-pixel, 18bpp or 24bpp. */ if (lvds_format_24bpp) {
val |= REG_LVDS_FMT_CHA_24BPP_MODE; if (ctx->lvds_dual_link)
val |= REG_LVDS_FMT_CHB_24BPP_MODE;
}
/* Set up LVDS format, JEIDA/Format 1 or SPWG/Format 2 */ if (lvds_format_jeida) {
val |= REG_LVDS_FMT_CHA_24BPP_FORMAT1; if (ctx->lvds_dual_link)
val |= REG_LVDS_FMT_CHB_24BPP_FORMAT1;
}
/* Set up LVDS output config (DSI84,DSI85) */ if (!ctx->lvds_dual_link)
val |= REG_LVDS_FMT_LVDS_LINK_CFG;
/* Clear all errors that got asserted during initialization. */
regmap_read(ctx->regmap, REG_IRQ_STAT, &pval);
regmap_write(ctx->regmap, REG_IRQ_STAT, pval);
/* Wait for 1ms and check for errors in status register */
usleep_range(1000, 1100);
regmap_read(ctx->regmap, REG_IRQ_STAT, &pval); if (pval)
dev_err(ctx->dev, "Unexpected link status 0x%02x\n", pval);
if (ctx->irq) { /* Enable irq to detect errors */
regmap_write(ctx->regmap, REG_IRQ_GLOBAL, REG_IRQ_GLOBAL_IRQ_EN);
regmap_write(ctx->regmap, REG_IRQ_EN, 0xff);
} else { /* Use the polling task */
sn65dsi83_monitor_start(ctx);
}
}
/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
gpiod_set_value_cansleep(ctx->enable_gpio, 0);
usleep_range(10000, 11000);
ret = regulator_disable(ctx->vcc); if (ret)
dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
for (i = 0; i <= 3; i++) { if (lvds_vod_swing_data_table[lvds_term][i][0] >= lvds_vod_swing_data[0] &&
lvds_vod_swing_data_table[lvds_term][i][1] <= lvds_vod_swing_data[1] &&
lvds_vod_swing_clock_table[lvds_term][i][0] >= lvds_vod_swing_clk[0] &&
lvds_vod_swing_clock_table[lvds_term][i][1] <= lvds_vod_swing_clk[1]) return i;
}
dev_err(dev, "failed to find appropriate LVDS_VOD_SWING configuration\n"); return -EINVAL;
}
staticint sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel)
{ struct device *dev = ctx->dev; struct device_node *endpoint; int endpoint_reg; /* Set so the property can be freely selected if not defined */
u32 lvds_vod_swing_data[2] = { 0, 1000000 };
u32 lvds_vod_swing_clk[2] = { 0, 1000000 }; /* Set default near end terminataion to 200 Ohm */
u32 lvds_term = 200; int lvds_vod_swing_conf; int ret = 0; int ret_data; int ret_clock;
ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt",
lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data)); if (ret_data != 0 && ret_data != -EINVAL) {
ret = ret_data; gotoexit;
}
ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt",
lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk)); if (ret_clock != 0 && ret_clock != -EINVAL) {
ret = ret_clock; gotoexit;
}
/* Use default value if both properties are NOT defined. */ if (ret_data == -EINVAL && ret_clock == -EINVAL)
lvds_vod_swing_conf = 0x1;
/* Use lookup table if any of the two properties is defined. */ if (!ret_data || !ret_clock) {
lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data,
lvds_vod_swing_clk, ctx->lvds_term_conf[channel]); if (lvds_vod_swing_conf < 0) {
ret = lvds_vod_swing_conf; gotoexit;
}
}
if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {
ctx->lvds_dual_link = true; /* Odd pixels to LVDS Channel A, even pixels to B */
ctx->lvds_dual_link_even_odd_swap = false;
} elseif (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
ctx->lvds_dual_link = true; /* Even pixels to LVDS Channel A, odd pixels to B */
ctx->lvds_dual_link_even_odd_swap = true;
}
}
panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0); if (IS_ERR(panel_bridge)) return dev_err_probe(dev, PTR_ERR(panel_bridge), "Failed to get panel bridge\n");
ctx->panel_bridge = panel_bridge;
ctx->vcc = devm_regulator_get(dev, "vcc"); if (IS_ERR(ctx->vcc)) return dev_err_probe(dev, PTR_ERR(ctx->vcc), "Failed to get supply 'vcc'\n");
if (dev->of_node) {
model = (enum sn65dsi83_model)(uintptr_t)
of_device_get_match_data(dev);
} else {
model = id->driver_data;
}
/* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable",
GPIOD_OUT_LOW); if (IS_ERR(ctx->enable_gpio)) return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio), "failed to get enable GPIO\n");
usleep_range(10000, 11000);
ret = sn65dsi83_parse_dt(ctx, model); if (ret) return ret;
ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config); if (IS_ERR(ctx->regmap)) return dev_err_probe(dev, PTR_ERR(ctx->regmap), "failed to get regmap\n");
if (client->irq) {
ctx->irq = client->irq;
ret = devm_request_threaded_irq(ctx->dev, ctx->irq, NULL, sn65dsi83_irq,
IRQF_ONESHOT, dev_name(ctx->dev), ctx); if (ret) return dev_err_probe(dev, ret, "failed to request irq\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.