On Sun, Jul 20, 2025 at 05:31:55PM +0530, Kaustabh Chakraborty wrote: > Synaptics TDDI (Touch/Display Integration) panels utilize a single chip > for display and touch controllers. Implement a simple device driver for > such panels, along with its built-in LED backlight controller, and add > support for TD4101 and TD4300 panels in the driver. > > Signed-off-by: Kaustabh Chakraborty <kauschl...@disroot.org> > --- > drivers/gpu/drm/panel/Kconfig | 11 + > drivers/gpu/drm/panel/Makefile | 1 + > drivers/gpu/drm/panel/panel-synaptics-tddi.c | 289 > +++++++++++++++++++++++++++ > 3 files changed, 301 insertions(+) > > + > +static int tddi_get_modes(struct drm_panel *panel, > + struct drm_connector *connector) > +{ > + struct tddi_ctx *ctx = to_tddi_ctx(panel); > + struct drm_display_mode *mode; > + > + mode = drm_mode_duplicate(connector->dev, &ctx->mode); > + if (!mode) > + return -ENOMEM; > + > + mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_probed_add(connector, mode); > + drm_mode_set_name(mode); > + > + connector->display_info.width_mm = mode->width_mm; > + connector->display_info.height_mm = mode->height_mm;
Please use drm_connector_helper_get_modes_fixed() > + connector->display_info.bus_flags = ctx->bus_flags; > + > + return 1; > +} > + > +static const struct backlight_ops tddi_bl_ops = { > + .update_status = tddi_update_status, > +}; > + > +static const struct drm_panel_funcs tddi_drm_panel_funcs = { > + .prepare = tddi_prepare, > + .unprepare = tddi_unprepare, > + .enable = tddi_enable, > + .disable = tddi_disable, > + .get_modes = tddi_get_modes, > +}; > + > +static int tddi_probe(struct mipi_dsi_device *dsi) > +{ > + struct device *dev = &dsi->dev; > + struct tddi_ctx *ctx; > + int ret; > + > + ctx = devm_drm_panel_alloc(dev, struct tddi_ctx, panel, > + &tddi_drm_panel_funcs, > DRM_MODE_CONNECTOR_DSI); > + if (IS_ERR(ctx)) > + return PTR_ERR(ctx); > + > + ctx->data = of_device_get_match_data(dev); > + > + ctx->dsi = dsi; > + mipi_dsi_set_drvdata(dsi, ctx); > + > + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(tddi_supplies), > + tddi_supplies, &ctx->supplies); > + if (ret < 0) > + return dev_err_probe(dev, ret, "failed to get regulators\n"); > + > + ctx->backlight_gpio = devm_gpiod_get_optional(dev, "backlight", > GPIOD_ASIS); > + if (IS_ERR(ctx->backlight_gpio)) > + return dev_err_probe(dev, PTR_ERR(ctx->backlight_gpio), > + "failed to get backlight-gpios\n"); > + > + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); > + if (IS_ERR(ctx->reset_gpio)) > + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), > + "failed to get reset-gpios\n"); > + > + ret = of_get_drm_panel_display_mode(dev->of_node, &ctx->mode, > + &ctx->bus_flags); > + if (ret < 0) > + return dev_err_probe(dev, ret, "failed to get panel timings\n"); > + > + ctx->backlight = devm_backlight_device_register(dev, dev_name(dev), dev, > + ctx, &tddi_bl_ops, > NULL); > + if (IS_ERR(ctx->backlight)) > + return dev_err_probe(dev, PTR_ERR(ctx->backlight), > + "failed to register backlight device"); > + > + ctx->backlight->props.type = BACKLIGHT_PLATFORM; > + ctx->backlight->props.brightness = 255; > + ctx->backlight->props.max_brightness = 255; Props should be set before registering the backlight > + > + dsi->lanes = ctx->data->lanes; > + dsi->format = MIPI_DSI_FMT_RGB888; > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | > + MIPI_DSI_MODE_VIDEO_NO_HFP; > + > + ctx->panel.prepare_prev_first = true; > + drm_panel_add(&ctx->panel); > + > + ret = devm_mipi_dsi_attach(dev, dsi); > + if (ret < 0) { > + drm_panel_remove(&ctx->panel); > + return dev_err_probe(dev, ret, "failed to attach to DSI > host\n"); > + } > + > + return 0; > +} > + > +static void tddi_remove(struct mipi_dsi_device *dsi) > +{ > + struct tddi_ctx *ctx = mipi_dsi_get_drvdata(dsi); > + > + drm_panel_remove(&ctx->panel); > +} > + > +static const struct tddi_panel_data td4101_panel_data = { > + .lanes = 2, > + /* wait timings for panel enable */ > + .delay_ms_sleep_exit = 100, > + .delay_ms_display_on = 0, > + /* wait timings for panel disable */ > + .delay_ms_display_off = 20, > + .delay_ms_sleep_enter = 90, > +}; > + > +static const struct tddi_panel_data td4300_panel_data = { > + .lanes = 4, > + /* wait timings for panel enable */ > + .delay_ms_sleep_exit = 100, > + .delay_ms_display_on = 0, > + /* wait timings for panel disable */ > + .delay_ms_display_off = 0, > + .delay_ms_sleep_enter = 0, > +}; > + > +static const struct of_device_id tddi_of_device_id[] = { > + { > + .compatible = "syna,td4101-panel", > + .data = &td4101_panel_data, > + }, { > + .compatible = "syna,td4300-panel", > + .data = &td4300_panel_data, > + }, { } > +}; > +MODULE_DEVICE_TABLE(of, tddi_of_device_id); > + > +static struct mipi_dsi_driver tddi_dsi_driver = { > + .probe = tddi_probe, > + .remove = tddi_remove, > + .driver = { > + .name = "panel-synaptics-tddi", > + .of_match_table = tddi_of_device_id, > + }, > +}; > +module_mipi_dsi_driver(tddi_dsi_driver); > + > +MODULE_AUTHOR("Kaustabh Chakraborty <kauschl...@disroot.org>"); > +MODULE_DESCRIPTION("Synaptics TDDI Display Panel Driver"); > +MODULE_LICENSE("GPL"); > > -- > 2.50.0 > -- With best wishes Dmitry