* [PATCH v2 0/2] drm/panel: Add Ilitek IL79900A controller and bindings @ 2025-10-10 9:37 Langyan Ye 2025-10-10 9:37 ` [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel Langyan Ye 2025-10-10 9:37 ` [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels Langyan Ye 0 siblings, 2 replies; 5+ messages in thread From: Langyan Ye @ 2025-10-10 9:37 UTC (permalink / raw) To: neil.armstrong, jessica.zhang, airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh, krzk+dt, conor+dt, dianders Cc: dri-devel, devicetree, linux-kernel, Langyan Ye This series adds device tree bindings and a DRM panel driver for the Ilitek IL79900A MIPI-DSI LCD controller, which is used in the Tianma TL121BVMS07-00 12.1-inch panel. Changes in v2: - PATCH 1/2: Address Rob Herring’s review comments and align with panel-common.yaml conventions. - PATCH 2/2: Rename driver to panel-ilitek-il79900a and align naming and structure with existing Ilitek panel drivers. - Link to v1: https://lore.kernel.org/all/20250930075044.1368134-1-yelangyan@huaqin.corp-partner.google.com/ Langyan Ye (2): dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel drm/panel: Add driver for Ilitek IL79900A-based panels .../display/panel/ilitek,il79900a.yaml | 64 +++ drivers/gpu/drm/panel/panel-ilitek-il79900a.c | 394 ++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml create mode 100644 drivers/gpu/drm/panel/panel-ilitek-il79900a.c -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel 2025-10-10 9:37 [PATCH v2 0/2] drm/panel: Add Ilitek IL79900A controller and bindings Langyan Ye @ 2025-10-10 9:37 ` Langyan Ye 2025-10-10 12:23 ` Rob Herring (Arm) 2025-10-10 9:37 ` [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels Langyan Ye 1 sibling, 1 reply; 5+ messages in thread From: Langyan Ye @ 2025-10-10 9:37 UTC (permalink / raw) To: neil.armstrong, jessica.zhang, airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh, krzk+dt, conor+dt, dianders Cc: dri-devel, devicetree, linux-kernel, Langyan Ye Add device tree bindings for the Tianma TL121BVMS07-00 12.1-inch MIPI-DSI TFT LCD panel. The panel is based on the Ilitek IL79900A controller. Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com> --- .../display/panel/ilitek,il79900a.yaml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml new file mode 100644 index 000000000000..a723f179efdf --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,il79900a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek IL79900a based MIPI-DSI panels + +maintainers: + - Langyan Ye <yelangyan@huaqin.corp-partner.google.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - tianma,il79900a + - const: ilitek,il79900a + + reg: true + + enable-gpios: + maxItems: 1 + description: GPIO specifier for the enable pin + + avdd-supply: + description: Positive analog voltage supply (AVDD) + + avee-supply: + description: Negative analog voltage supply (AVEE) + + pp1800-supply: + description: 1.8V logic voltage supply + +required: + - compatible + - reg + - enable-gpios + - avdd-supply + - avee-supply + - pp1800-supply + +additionalProperties: false + +examples: + - | + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "tianma,il79900a", "ilitek,il79900a"; + reg = <0>; + enable-gpios = <&pio 25 0>; + avdd-supply = <®_avdd>; + avee-supply = <®_avee>; + pp1800-supply = <®_pp1800>; + backlight = <&backlight>; + }; + }; + +... -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel 2025-10-10 9:37 ` [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel Langyan Ye @ 2025-10-10 12:23 ` Rob Herring (Arm) 0 siblings, 0 replies; 5+ messages in thread From: Rob Herring (Arm) @ 2025-10-10 12:23 UTC (permalink / raw) To: Langyan Ye Cc: mripard, devicetree, jessica.zhang, linux-kernel, dri-devel, airlied, neil.armstrong, tzimmermann, dianders, simona, maarten.lankhorst, conor+dt, krzk+dt On Fri, 10 Oct 2025 17:37:50 +0800, Langyan Ye wrote: > Add device tree bindings for the Tianma TL121BVMS07-00 12.1-inch > MIPI-DSI TFT LCD panel. The panel is based on the Ilitek IL79900A > controller. > > Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com> > --- > .../display/panel/ilitek,il79900a.yaml | 64 +++++++++++++++++++ > 1 file changed, 64 insertions(+) > create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.example.dtb: panel@0 (tianma,il79900a): 'backlight' does not match any of the regexes: '^pinctrl-[0-9]+$' from schema $id: http://devicetree.org/schemas/display/panel/ilitek,il79900a.yaml# doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20251010093751.2793492-2-yelangyan@huaqin.corp-partner.google.com The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels 2025-10-10 9:37 [PATCH v2 0/2] drm/panel: Add Ilitek IL79900A controller and bindings Langyan Ye 2025-10-10 9:37 ` [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel Langyan Ye @ 2025-10-10 9:37 ` Langyan Ye 2025-10-10 12:29 ` Dmitry Baryshkov 1 sibling, 1 reply; 5+ messages in thread From: Langyan Ye @ 2025-10-10 9:37 UTC (permalink / raw) To: neil.armstrong, jessica.zhang, airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh, krzk+dt, conor+dt, dianders Cc: dri-devel, devicetree, linux-kernel, Langyan Ye Add a DRM panel driver for the Ilitek IL79900A MIPI-DSI LCD controller. The controller is used in panels such as the Tianma TL121BVMS07-00. It requires multiple power supplies (AVDD, AVEE, 1.8V logic), an enable GPIO, and a backlight device. Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com> --- drivers/gpu/drm/panel/panel-ilitek-il79900a.c | 394 ++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-ilitek-il79900a.c diff --git a/drivers/gpu/drm/panel/panel-ilitek-il79900a.c b/drivers/gpu/drm/panel/panel-ilitek-il79900a.c new file mode 100644 index 000000000000..2134f8af673e --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-il79900a.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Panels based on the Ilitek IL79900A display controller. + */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <video/mipi_display.h> + +struct il79900a; + +/* + * Use this descriptor struct to describe different panels using the + * Ilitek IL79900A display controller. + */ +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + + /** + * @width_mm: width of the panel's active display area + * @height_mm: height of the panel's active display area + */ + struct { + unsigned int width_mm; + unsigned int height_mm; + } size; + + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + int (*init)(struct il79900a *boe); + unsigned int lanes; +}; + +struct il79900a { + struct drm_panel base; + struct mipi_dsi_device *dsi; + + const struct panel_desc *desc; + + enum drm_panel_orientation orientation; + struct regulator *pp1800; + struct regulator *avee; + struct regulator *avdd; + struct gpio_desc *enable_gpio; +}; + +/* IL79900A-specific commands, add new commands as you decode them */ +#define IL79900A_DCS_SWITCH_PAGE 0xFF + +#define il79900a_switch_page(ctx, page) \ + mipi_dsi_dcs_write_seq_multi(ctx, IL79900A_DCS_SWITCH_PAGE, \ + 0x5a, 0xa5, (page)) + +static int tianma_il79900a_init(struct il79900a *ili) +{ + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; + + usleep_range(5000, 5100); + + il79900a_switch_page(&ctx, 0x06); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3e, 0x62); + + il79900a_switch_page(&ctx, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x1b, 0x20); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5d, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5e, 0x40); + + il79900a_switch_page(&ctx, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0X29, 0x00); + + il79900a_switch_page(&ctx, 0x06); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x92, 0x22); + + il79900a_switch_page(&ctx, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_EXIT_SLEEP_MODE); + if (ctx.accum_err) + return ctx.accum_err; + + msleep(120); + + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_DISPLAY_ON); + if (ctx.accum_err) + return ctx.accum_err; + + msleep(80); + + return 0; +}; + +static inline struct il79900a *to_il79900a(struct drm_panel *panel) +{ + return container_of(panel, struct il79900a, base); +} + +static int il79900a_enter_sleep_mode(struct il79900a *ili) +{ + struct mipi_dsi_device *dsi = ili->dsi; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + return ret; + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + return ret; + + return 0; +} + +static int il79900a_disable(struct drm_panel *panel) +{ + struct il79900a *ili = to_il79900a(panel); + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; + int ret; + + il79900a_switch_page(&ctx, 0x00); + if (ctx.accum_err) + return ctx.accum_err; + + ret = il79900a_enter_sleep_mode(ili); + if (ret < 0) { + dev_err(panel->dev, "failed to set panel off: %d\n", ret); + return ret; + } + + msleep(150); + + return 0; +} + +static int il79900a_unprepare(struct drm_panel *panel) +{ + struct il79900a *ili = to_il79900a(panel); + + gpiod_set_value(ili->enable_gpio, 0); + usleep_range(1000, 2000); + regulator_disable(ili->avee); + regulator_disable(ili->avdd); + usleep_range(5000, 7000); + regulator_disable(ili->pp1800); + + return 0; +} + +static int il79900a_prepare(struct drm_panel *panel) +{ + struct il79900a *ili = to_il79900a(panel); + int ret; + + gpiod_set_value(ili->enable_gpio, 0); + usleep_range(1000, 1500); + + ret = regulator_enable(ili->pp1800); + if (ret < 0) + return ret; + + usleep_range(3000, 5000); + + ret = regulator_enable(ili->avdd); + if (ret < 0) + goto poweroff1v8; + ret = regulator_enable(ili->avee); + if (ret < 0) + goto poweroffavdd; + + usleep_range(10000, 11000); + + // MIPI needs to keep the LP11 state before the lcm_reset pin is pulled high + ret = mipi_dsi_dcs_nop(ili->dsi); + if (ret < 0) { + dev_err(&ili->dsi->dev, "Failed to send NOP: %d\n", ret); + goto poweroff; + } + usleep_range(1000, 2000); + + gpiod_set_value(ili->enable_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(ili->enable_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(ili->enable_gpio, 1); + usleep_range(20000, 21000); + + ret = ili->desc->init(ili); + if (ret < 0) + goto poweroff; + + return 0; + +poweroff: + gpiod_set_value(ili->enable_gpio, 0); + regulator_disable(ili->avee); +poweroffavdd: + regulator_disable(ili->avdd); +poweroff1v8: + usleep_range(5000, 7000); + regulator_disable(ili->pp1800); + + return ret; +} + +static int il79900a_enable(struct drm_panel *panel) +{ + msleep(130); + return 0; +} + +static const struct drm_display_mode tianma_il79900a_default_mode = { + .clock = 264355, + .hdisplay = 1600, + .hsync_start = 1600 + 20, + .hsync_end = 1600 + 20 + 4, + .htotal = 1600 + 20 + 4 + 20, + .vdisplay = 2560, + .vsync_start = 2560 + 82, + .vsync_end = 2560 + 82 + 2, + .vtotal = 2560 + 82 + 2 + 36, +}; + +static const struct panel_desc tianma_il79900a_desc = { + .modes = &tianma_il79900a_default_mode, + .bpc = 8, + .size = { + .width_mm = 163, + .height_mm = 260, + }, + .lanes = 3, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init = tianma_il79900a_init, +}; + +static int il79900a_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct il79900a *ili = to_il79900a(panel); + const struct drm_display_mode *m = ili->desc->modes; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, m); + if (!mode) { + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", + m->hdisplay, m->vdisplay, drm_mode_vrefresh(m)); + return -ENOMEM; + } + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + connector->display_info.width_mm = ili->desc->size.width_mm; + connector->display_info.height_mm = ili->desc->size.height_mm; + connector->display_info.bpc = ili->desc->bpc; + + return 1; +} + +static enum drm_panel_orientation il79900a_get_orientation(struct drm_panel *panel) +{ + struct il79900a *ili = to_il79900a(panel); + + return ili->orientation; +} + +static const struct drm_panel_funcs il79900a_funcs = { + .disable = il79900a_disable, + .unprepare = il79900a_unprepare, + .prepare = il79900a_prepare, + .enable = il79900a_enable, + .get_modes = il79900a_get_modes, + .get_orientation = il79900a_get_orientation, +}; + +static int il79900a_add(struct il79900a *ili) +{ + struct device *dev = &ili->dsi->dev; + int err; + + ili->avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(ili->avdd)) + return PTR_ERR(ili->avdd); + + ili->avee = devm_regulator_get(dev, "avee"); + if (IS_ERR(ili->avee)) + return PTR_ERR(ili->avee); + + ili->pp1800 = devm_regulator_get(dev, "pp1800"); + if (IS_ERR(ili->pp1800)) + return PTR_ERR(ili->pp1800); + + ili->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(ili->enable_gpio)) { + dev_err(dev, "cannot get reset-gpios %ld\n", + PTR_ERR(ili->enable_gpio)); + return PTR_ERR(ili->enable_gpio); + } + + gpiod_set_value(ili->enable_gpio, 0); + + drm_panel_init(&ili->base, dev, &il79900a_funcs, + DRM_MODE_CONNECTOR_DSI); + err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); + if (err < 0) { + dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); + return err; + } + + err = drm_panel_of_backlight(&ili->base); + if (err) + return err; + + ili->base.funcs = &il79900a_funcs; + ili->base.dev = &ili->dsi->dev; + + drm_panel_add(&ili->base); + + return 0; +} + +static int il79900a_probe(struct mipi_dsi_device *dsi) +{ + struct il79900a *ili; + int ret; + const struct panel_desc *desc; + + ili = devm_kzalloc(&dsi->dev, sizeof(*ili), GFP_KERNEL); + if (!ili) + return -ENOMEM; + + desc = of_device_get_match_data(&dsi->dev); + dsi->lanes = desc->lanes; + dsi->format = desc->format; + dsi->mode_flags = desc->mode_flags; + ili->desc = desc; + ili->dsi = dsi; + ret = il79900a_add(ili); + if (ret < 0) + return ret; + + mipi_dsi_set_drvdata(dsi, ili); + + ret = mipi_dsi_attach(dsi); + if (ret) + drm_panel_remove(&ili->base); + + return ret; +} + +static void il79900a_remove(struct mipi_dsi_device *dsi) +{ + struct il79900a *ili = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); + + if (ili->base.dev) + drm_panel_remove(&ili->base); +} + +static const struct of_device_id il79900a_of_match[] = { + { .compatible = "tianma,il79900a", + .data = &tianma_il79900a_desc + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, il79900a_of_match); + +static struct mipi_dsi_driver il79900a_driver = { + .driver = { + .name = "panel-il79900a", + .of_match_table = il79900a_of_match, + }, + .probe = il79900a_probe, + .remove = il79900a_remove, +}; +module_mipi_dsi_driver(il79900a_driver); + +MODULE_AUTHOR("Langyan Ye <yelangyan@huaqin.corp-partner.google.com>"); +MODULE_DESCRIPTION("Ilitek IL79900A-based panels driver"); +MODULE_LICENSE("GPL"); -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels 2025-10-10 9:37 ` [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels Langyan Ye @ 2025-10-10 12:29 ` Dmitry Baryshkov 0 siblings, 0 replies; 5+ messages in thread From: Dmitry Baryshkov @ 2025-10-10 12:29 UTC (permalink / raw) To: Langyan Ye Cc: neil.armstrong, jessica.zhang, airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh, krzk+dt, conor+dt, dianders, dri-devel, devicetree, linux-kernel On Fri, Oct 10, 2025 at 05:37:51PM +0800, Langyan Ye wrote: > Add a DRM panel driver for the Ilitek IL79900A MIPI-DSI LCD controller. > > The controller is used in panels such as the Tianma TL121BVMS07-00. > It requires multiple power supplies (AVDD, AVEE, 1.8V logic), an enable > GPIO, and a backlight device. > > Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com> > --- > drivers/gpu/drm/panel/panel-ilitek-il79900a.c | 394 ++++++++++++++++++ > 1 file changed, 394 insertions(+) > create mode 100644 drivers/gpu/drm/panel/panel-ilitek-il79900a.c > > diff --git a/drivers/gpu/drm/panel/panel-ilitek-il79900a.c b/drivers/gpu/drm/panel/panel-ilitek-il79900a.c > new file mode 100644 > index 000000000000..2134f8af673e > --- /dev/null > +++ b/drivers/gpu/drm/panel/panel-ilitek-il79900a.c > @@ -0,0 +1,394 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Panels based on the Ilitek IL79900A display controller. > + */ > +#include <linux/delay.h> > +#include <linux/gpio/consumer.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/regulator/consumer.h> > + > +#include <drm/drm_connector.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_mipi_dsi.h> > +#include <drm/drm_panel.h> > + > +#include <video/mipi_display.h> > + > +struct il79900a; > + > +/* > + * Use this descriptor struct to describe different panels using the > + * Ilitek IL79900A display controller. > + */ > +struct panel_desc { > + const struct drm_display_mode *modes; > + unsigned int bpc; > + > + /** > + * @width_mm: width of the panel's active display area > + * @height_mm: height of the panel's active display area > + */ > + struct { > + unsigned int width_mm; > + unsigned int height_mm; > + } size; Move these to the mode > + > + unsigned long mode_flags; > + enum mipi_dsi_pixel_format format; > + int (*init)(struct il79900a *boe); > + unsigned int lanes; > +}; > + > +struct il79900a { > + struct drm_panel base; > + struct mipi_dsi_device *dsi; > + > + const struct panel_desc *desc; > + > + enum drm_panel_orientation orientation; > + struct regulator *pp1800; > + struct regulator *avee; > + struct regulator *avdd; > + struct gpio_desc *enable_gpio; > +}; > + > +/* IL79900A-specific commands, add new commands as you decode them */ > +#define IL79900A_DCS_SWITCH_PAGE 0xFF > + > +#define il79900a_switch_page(ctx, page) \ > + mipi_dsi_dcs_write_seq_multi(ctx, IL79900A_DCS_SWITCH_PAGE, \ > + 0x5a, 0xa5, (page)) > + > +static int tianma_il79900a_init(struct il79900a *ili) > +{ > + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; > + > + usleep_range(5000, 5100); mipi_dsi_usleep_range > + > + il79900a_switch_page(&ctx, 0x06); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3e, 0x62); > + > + il79900a_switch_page(&ctx, 0x02); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0x1b, 0x20); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5d, 0x00); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5e, 0x40); > + > + il79900a_switch_page(&ctx, 0x07); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0X29, 0x00); > + > + il79900a_switch_page(&ctx, 0x06); > + mipi_dsi_dcs_write_seq_multi(&ctx, 0x92, 0x22); > + > + il79900a_switch_page(&ctx, 0x00); > + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_EXIT_SLEEP_MODE); > + if (ctx.accum_err) > + return ctx.accum_err; Drop > + > + msleep(120); mipi_dsi_msleep > + > + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_DISPLAY_ON); > + if (ctx.accum_err) > + return ctx.accum_err; drop > + > + msleep(80); mipi_dsi_msleep > + > + return 0; > +}; > + > +static inline struct il79900a *to_il79900a(struct drm_panel *panel) > +{ > + return container_of(panel, struct il79900a, base); > +} > + > +static int il79900a_enter_sleep_mode(struct il79900a *ili) > +{ > + struct mipi_dsi_device *dsi = ili->dsi; > + int ret; > + > + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; > + > + ret = mipi_dsi_dcs_set_display_off(dsi); _multi > + if (ret < 0) > + return ret; > + > + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); _multi > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int il79900a_disable(struct drm_panel *panel) > +{ > + struct il79900a *ili = to_il79900a(panel); > + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; > + int ret; > + > + il79900a_switch_page(&ctx, 0x00); > + if (ctx.accum_err) > + return ctx.accum_err; > + > + ret = il79900a_enter_sleep_mode(ili); Pass context to that function > + if (ret < 0) { > + dev_err(panel->dev, "failed to set panel off: %d\n", ret); > + return ret; > + } > + > + msleep(150); mipi_dsi_msleep > + > + return 0; > +} > + > +static int il79900a_unprepare(struct drm_panel *panel) > +{ > + struct il79900a *ili = to_il79900a(panel); > + > + gpiod_set_value(ili->enable_gpio, 0); > + usleep_range(1000, 2000); > + regulator_disable(ili->avee); > + regulator_disable(ili->avdd); > + usleep_range(5000, 7000); > + regulator_disable(ili->pp1800); > + > + return 0; > +} > + > +static int il79900a_prepare(struct drm_panel *panel) > +{ > + struct il79900a *ili = to_il79900a(panel); > + int ret; > + > + gpiod_set_value(ili->enable_gpio, 0); > + usleep_range(1000, 1500); > + > + ret = regulator_enable(ili->pp1800); > + if (ret < 0) > + return ret; > + > + usleep_range(3000, 5000); > + > + ret = regulator_enable(ili->avdd); > + if (ret < 0) > + goto poweroff1v8; > + ret = regulator_enable(ili->avee); > + if (ret < 0) > + goto poweroffavdd; > + > + usleep_range(10000, 11000); > + > + // MIPI needs to keep the LP11 state before the lcm_reset pin is pulled high > + ret = mipi_dsi_dcs_nop(ili->dsi); > + if (ret < 0) { > + dev_err(&ili->dsi->dev, "Failed to send NOP: %d\n", ret); > + goto poweroff; > + } > + usleep_range(1000, 2000); > + > + gpiod_set_value(ili->enable_gpio, 1); > + usleep_range(1000, 2000); > + gpiod_set_value(ili->enable_gpio, 0); > + usleep_range(10000, 11000); > + gpiod_set_value(ili->enable_gpio, 1); > + usleep_range(20000, 21000); > + > + ret = ili->desc->init(ili); > + if (ret < 0) > + goto poweroff; > + > + return 0; > + > +poweroff: > + gpiod_set_value(ili->enable_gpio, 0); > + regulator_disable(ili->avee); > +poweroffavdd: > + regulator_disable(ili->avdd); > +poweroff1v8: > + usleep_range(5000, 7000); > + regulator_disable(ili->pp1800); > + > + return ret; > +} > + > +static int il79900a_enable(struct drm_panel *panel) > +{ > + msleep(130); Why? > + return 0; > +} > + > +static const struct drm_display_mode tianma_il79900a_default_mode = { > + .clock = 264355, > + .hdisplay = 1600, > + .hsync_start = 1600 + 20, > + .hsync_end = 1600 + 20 + 4, > + .htotal = 1600 + 20 + 4 + 20, > + .vdisplay = 2560, > + .vsync_start = 2560 + 82, > + .vsync_end = 2560 + 82 + 2, > + .vtotal = 2560 + 82 + 2 + 36, > +}; > + > +static const struct panel_desc tianma_il79900a_desc = { > + .modes = &tianma_il79900a_default_mode, > + .bpc = 8, > + .size = { > + .width_mm = 163, > + .height_mm = 260, > + }, > + .lanes = 3, > + .format = MIPI_DSI_FMT_RGB888, > + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | > + MIPI_DSI_MODE_LPM, > + .init = tianma_il79900a_init, > +}; > + > +static int il79900a_get_modes(struct drm_panel *panel, > + struct drm_connector *connector) > +{ > + struct il79900a *ili = to_il79900a(panel); > + const struct drm_display_mode *m = ili->desc->modes; > + struct drm_display_mode *mode; > + > + mode = drm_mode_duplicate(connector->dev, m); > + if (!mode) { > + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", > + m->hdisplay, m->vdisplay, drm_mode_vrefresh(m)); > + return -ENOMEM; > + } > + > + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_set_name(mode); > + drm_mode_probed_add(connector, mode); > + > + connector->display_info.width_mm = ili->desc->size.width_mm; > + connector->display_info.height_mm = ili->desc->size.height_mm; drm_connector_helper_get_modes_fixed() > + connector->display_info.bpc = ili->desc->bpc; > + > + return 1; > +} > + > +static enum drm_panel_orientation il79900a_get_orientation(struct drm_panel *panel) > +{ > + struct il79900a *ili = to_il79900a(panel); > + > + return ili->orientation; > +} > + > +static const struct drm_panel_funcs il79900a_funcs = { > + .disable = il79900a_disable, > + .unprepare = il79900a_unprepare, > + .prepare = il79900a_prepare, > + .enable = il79900a_enable, > + .get_modes = il79900a_get_modes, > + .get_orientation = il79900a_get_orientation, > +}; > + > +static int il79900a_add(struct il79900a *ili) > +{ > + struct device *dev = &ili->dsi->dev; > + int err; > + > + ili->avdd = devm_regulator_get(dev, "avdd"); > + if (IS_ERR(ili->avdd)) > + return PTR_ERR(ili->avdd); > + > + ili->avee = devm_regulator_get(dev, "avee"); > + if (IS_ERR(ili->avee)) > + return PTR_ERR(ili->avee); > + > + ili->pp1800 = devm_regulator_get(dev, "pp1800"); > + if (IS_ERR(ili->pp1800)) > + return PTR_ERR(ili->pp1800); > + > + ili->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); > + if (IS_ERR(ili->enable_gpio)) { > + dev_err(dev, "cannot get reset-gpios %ld\n", > + PTR_ERR(ili->enable_gpio)); > + return PTR_ERR(ili->enable_gpio); > + } > + > + gpiod_set_value(ili->enable_gpio, 0); > + > + drm_panel_init(&ili->base, dev, &il79900a_funcs, > + DRM_MODE_CONNECTOR_DSI); > + err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); > + if (err < 0) { > + dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err); > + return err; > + } > + > + err = drm_panel_of_backlight(&ili->base); > + if (err) > + return err; > + > + ili->base.funcs = &il79900a_funcs; > + ili->base.dev = &ili->dsi->dev; > + > + drm_panel_add(&ili->base); > + > + return 0; > +} > + > +static int il79900a_probe(struct mipi_dsi_device *dsi) > +{ > + struct il79900a *ili; > + int ret; > + const struct panel_desc *desc; > + > + ili = devm_kzalloc(&dsi->dev, sizeof(*ili), GFP_KERNEL); > + if (!ili) > + return -ENOMEM; > + > + desc = of_device_get_match_data(&dsi->dev); > + dsi->lanes = desc->lanes; > + dsi->format = desc->format; > + dsi->mode_flags = desc->mode_flags; > + ili->desc = desc; > + ili->dsi = dsi; > + ret = il79900a_add(ili); > + if (ret < 0) > + return ret; > + > + mipi_dsi_set_drvdata(dsi, ili); > + > + ret = mipi_dsi_attach(dsi); > + if (ret) > + drm_panel_remove(&ili->base); > + > + return ret; > +} > + > +static void il79900a_remove(struct mipi_dsi_device *dsi) > +{ > + struct il79900a *ili = mipi_dsi_get_drvdata(dsi); > + int ret; > + > + ret = mipi_dsi_detach(dsi); > + if (ret < 0) > + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); > + > + if (ili->base.dev) > + drm_panel_remove(&ili->base); > +} > + > +static const struct of_device_id il79900a_of_match[] = { > + { .compatible = "tianma,il79900a", > + .data = &tianma_il79900a_desc > + }, Single line > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, il79900a_of_match); > + > +static struct mipi_dsi_driver il79900a_driver = { > + .driver = { > + .name = "panel-il79900a", > + .of_match_table = il79900a_of_match, > + }, > + .probe = il79900a_probe, > + .remove = il79900a_remove, > +}; > +module_mipi_dsi_driver(il79900a_driver); > + > +MODULE_AUTHOR("Langyan Ye <yelangyan@huaqin.corp-partner.google.com>"); > +MODULE_DESCRIPTION("Ilitek IL79900A-based panels driver"); > +MODULE_LICENSE("GPL"); > -- > 2.34.1 > -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-10-10 12:29 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-10-10 9:37 [PATCH v2 0/2] drm/panel: Add Ilitek IL79900A controller and bindings Langyan Ye 2025-10-10 9:37 ` [PATCH v2 1/2] dt-bindings: display: panel: Add Tianma TL121BVMS07-00 panel Langyan Ye 2025-10-10 12:23 ` Rob Herring (Arm) 2025-10-10 9:37 ` [PATCH v2 2/2] drm/panel: Add driver for Ilitek IL79900A-based panels Langyan Ye 2025-10-10 12:29 ` Dmitry Baryshkov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®