Re: [PATCH 2/2] drm/panel: Add driver for Novatek NT37703 based panels

Neil Armstrong <[email protected]>
Newsgroups org.kernel.vger.phone-devel,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel
Organization Linaro
Message-ID <[email protected]>
On 8/11/26 07:52, Esteban Urrutia via B4 Relay wrote:
> From: Esteban Urrutia <[email protected]>
> 
> Add driver for panels based on this DDIC.
> Initial support is added for the Tianma "v2" panel (no panel model) found
> in the Motorola ThinkPhone by motorola.
> 
> Signed-off-by: Esteban Urrutia <[email protected]>
> ---
>   MAINTAINERS                                   |   6 +
>   drivers/gpu/drm/panel/Kconfig                 |  13 +
>   drivers/gpu/drm/panel/Makefile                |   1 +
>   drivers/gpu/drm/panel/panel-novatek-nt37703.c | 467 ++++++++++++++++++++++++++
>   4 files changed, 487 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index da14a58df9e8..e2bb50a8b1af 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8339,6 +8339,12 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
>   F:	Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml
>   F:	drivers/gpu/drm/panel/panel-novatek-nt36672a.c
>   
> +DRM DRIVER FOR NOVATEK NT37703 PANELS
> +M:	Esteban Urrutia <[email protected]>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/display/panel/novatek,nt37703.yaml
> +F:	drivers/gpu/drm/panel/panel-novatek-nt37703.c
> +
>   DRM DRIVER FOR NOVATEK NT37801 PANELS
>   M:	Krzysztof Kozlowski <[email protected]>
>   S:	Maintained
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index cbdf7b8f7f7a..9cd74ceef44e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -706,6 +706,19 @@ config DRM_PANEL_NOVATEK_NT37700F
>   	  Say Y here if you want to enable support for Novatek NT37700F DSI
>   	  panel module. The panel has a resolution of 1080x2160.
>   
> +config DRM_PANEL_NOVATEK_NT37703
> +	tristate "Novatek NT37703 DSI panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	select DRM_DISPLAY_DSC_HELPER
> +	select DRM_DISPLAY_HELPER
> +	help
> +	  Say Y or M here if you want to enable support for panels based
> +	  on the Novatek NT37703 display driver IC, such as Tianma
> +	  panels found in some units of the Motorola ThinkPhone by
> +	  motorola.
> +
>   config DRM_PANEL_NOVATEK_NT37801
>   	tristate "Novatek NT37801/NT37810 AMOLED DSI panel"
>   	depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 3b523cf37833..4a69b2657c5b 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.o
>   obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
>   obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o
>   obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o
> +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37703) += panel-novatek-nt37703.o
>   obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o
>   obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
>   obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
> diff --git a/drivers/gpu/drm/panel/panel-novatek-nt37703.c b/drivers/gpu/drm/panel/panel-novatek-nt37703.c
> new file mode 100644
> index 000000000000..5858bf12a310
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-novatek-nt37703.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Driver for Novatek NT37703 based MIPI DSI panels
> + *
> + * Multiple panel support based on Novatek NT36523 driver
> + * Per panel DSC support based on Ilitek ILI9882T driver
> + *
> + * Copyright (c) 2026 Esteban Urrutia <[email protected]>
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/mipi_display.h>
> +
> +#include <drm/display/drm_dsc.h>
> +#include <drm/display/drm_dsc_helper.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#define DSC_BPG_OFFSET(x)		((u8) ((x) & DSC_RANGE_BPG_OFFSET_MASK))
> +#define NT37703_DCS_SWITCH_PAGE		0xf0
> +
> +#define nt37703_cmd_test_on(ctx)	mipi_dsi_dcs_write_seq_multi((ctx), 0xff, 0xaa, 0x55, \
> +								     0xa5, 0x80)
> +#define nt37703_switch_page(ctx, page)	mipi_dsi_dcs_write_seq_multi((ctx), \
> +								     NT37703_DCS_SWITCH_PAGE, \
> +								     0x55, 0xaa, 0x52, 0x08, (page))
> +
> +struct panel_info {
> +	struct drm_panel panel;
> +	const struct panel_desc *desc;
> +	struct mipi_dsi_device *dsi;
> +	struct drm_dsc_config dsc;
> +	struct regulator_bulk_data *supplies;
> +	struct gpio_desc *reset_gpio;
> +};
> +
> +struct panel_desc {
> +	unsigned int width_mm;
> +	unsigned int height_mm;
> +
> +	unsigned int lanes;
> +	unsigned long mode_flags;
> +	enum mipi_dsi_pixel_format format;
> +
> +	const struct drm_display_mode *modes;
> +	unsigned int num_modes;
> +	int (*init_sequence)(struct panel_info *pinfo);
> +	const struct drm_dsc_config *dsc;
> +};
> +
> +/*
> + * These parameters are applied when using DSC 1.1 and when the DSC configuration specifies 10 b.p.c
> + * and 8 b.p.p
> + */
> +static const struct drm_dsc_rc_range_parameters nt37703_rc_range_params[DSC_NUM_BUF_RANGES] = {
> +	{ 0,  8,  DSC_BPG_OFFSET(2)   },
> +	{ 4,  8,  DSC_BPG_OFFSET(0)   },
> +	{ 5,  9,  DSC_BPG_OFFSET(0)   },
> +	{ 5,  10, DSC_BPG_OFFSET(-2)  },
> +	{ 7,  11, DSC_BPG_OFFSET(-4)  },
> +	{ 7,  11, DSC_BPG_OFFSET(-6)  },
> +	{ 7,  11, DSC_BPG_OFFSET(-8)  },
> +	{ 7,  12, DSC_BPG_OFFSET(-8)  },
> +	{ 7,  13, DSC_BPG_OFFSET(-8)  },
> +	{ 8,  14, DSC_BPG_OFFSET(-10) },
> +	{ 9,  14, DSC_BPG_OFFSET(-10) },
> +	{ 9,  15, DSC_BPG_OFFSET(-10) },
> +	{ 9,  15, DSC_BPG_OFFSET(-12) },
> +	{ 12, 16, DSC_BPG_OFFSET(-12) },
> +	{ 16, 17, DSC_BPG_OFFSET(-12) },
> +};
> +
> +static const struct regulator_bulk_data nt37703_supplies[] = {
> +	{ .supply = "avdd"  },
> +	{ .supply = "elvdd" },
> +	{ .supply = "elvss" },
> +	{ .supply = "vci"   },
> +	{ .supply = "vdd"   },
> +	{ .supply = "vddi"  },
> +};
> +
> +static inline struct panel_info *to_panel_info(struct drm_panel *panel)
> +{
> +	return container_of_const(panel, struct panel_info, panel);
> +}
> +
> +static void nt37703_reset(struct panel_info *pinfo)
> +{
> +	gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
> +	usleep_range(1000, 2000);
> +	gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
> +	usleep_range(1000, 2000);
> +	gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
> +	usleep_range(10000, 11000);
> +}
> +
> +static int nt37703_off(struct panel_info *pinfo)
> +{
> +	struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
> +
> +	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
> +	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
> +	mipi_dsi_msleep(&dsi_ctx, 120);
> +
> +	return dsi_ctx.accum_err;
> +}
> +
> +static int nt37703_prepare(struct drm_panel *panel)
> +{
> +	struct panel_info *pinfo = to_panel_info(panel);
> +	struct device *dev = &pinfo->dsi->dev;
> +	struct drm_dsc_picture_parameter_set pps;
> +	int ret;
> +	int i;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable regulators: %d\n", ret);
> +		return ret;
> +	}
> +
> +	nt37703_reset(pinfo);
> +
> +	ret = pinfo->desc->init_sequence(pinfo);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to initialize panel: %d\n", ret);
> +		gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
> +		regulator_bulk_disable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
> +		return ret;
> +	}
> +
> +	/*
> +	 * For some reason this DDIC uses different parameters for the RC ranges under certain DSC
> +	 * configurations.
> +	 * Not applying them results in heavy visual glitches, so do so if all conditions are met.
> +	 */
> +	if (pinfo->dsc.dsc_version_major == 1 && pinfo->dsc.dsc_version_minor == 1 &&
> +	    pinfo->dsc.bits_per_component == 10 && pinfo->dsc.bits_per_pixel == 8 << 4) {
> +		for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
> +			pinfo->dsc.rc_range_params[i].range_min_qp =
> +				nt37703_rc_range_params[i].range_min_qp;
> +			pinfo->dsc.rc_range_params[i].range_max_qp =
> +				nt37703_rc_range_params[i].range_max_qp;
> +			pinfo->dsc.rc_range_params[i].range_bpg_offset =
> +				nt37703_rc_range_params[i].range_bpg_offset;
> +		}
> +	}
> +
> +	/*
> +	 * Given drm_dsc_setup_rc_params() inconditionally overrides certain members in pinfo->dsc
> +	 * that may have been set in pinfo->desc->dsc, override these members once more with what's
> +	 * found in pinfo->desc->dsc.
> +	 */
> +	if (pinfo->desc->dsc->first_line_bpg_offset)
> +		pinfo->dsc.first_line_bpg_offset = pinfo->desc->dsc->first_line_bpg_offset;
> +
> +	drm_dsc_pps_payload_pack(&pps, &pinfo->dsc);
> +
> +	ret = mipi_dsi_picture_parameter_set(pinfo->dsi, &pps);
> +	if (ret < 0) {
> +		dev_err(panel->dev, "Failed to transmit PPS: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = mipi_dsi_compression_mode(pinfo->dsi, true);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable compression mode: %d\n", ret);
> +		return ret;
> +	}
> +
> +	msleep(28);
> +
> +	return 0;
> +}
> +
> +static int nt37703_unprepare(struct drm_panel *panel)
> +{
> +	struct panel_info *pinfo = to_panel_info(panel);
> +	struct device *dev = &pinfo->dsi->dev;
> +	int ret;
> +
> +	ret = nt37703_off(pinfo);
> +	if (ret < 0)
> +		dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
> +
> +	gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
> +	regulator_bulk_disable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
> +
> +	return 0;
> +}
> +
> +static int nt37703_get_modes(struct drm_panel *panel, struct drm_connector *connector)
> +{
> +	struct panel_info *pinfo = to_panel_info(panel);
> +	int i;
> +
> +	for (i = 0; i < pinfo->desc->num_modes; i++) {
> +		const struct drm_display_mode *m = &pinfo->desc->modes[i];
> +		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;
> +		if (i == 0)
> +			mode->type |= DRM_MODE_TYPE_PREFERRED;
> +
> +		drm_mode_set_name(mode);
> +		drm_mode_probed_add(connector, mode);
> +	}
> +
> +	connector->display_info.width_mm = pinfo->desc->width_mm;
> +	connector->display_info.height_mm = pinfo->desc->height_mm;
> +
> +	return pinfo->desc->num_modes;

The only supported panel only supports a single mode, just support
a single mode for now and drop this open coded support for multiple modes.

You can add it later if somehow you need multiple modes.

> +}
> +
> +static const struct drm_panel_funcs nt37703_panel_funcs = {
> +	.prepare = nt37703_prepare,
> +	.unprepare = nt37703_unprepare,
> +	.get_modes = nt37703_get_modes,
> +};
> +
> +static int nt37703_bl_update_status(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	u16 brightness = backlight_get_brightness(bl);
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	return 0;
> +}
> +
> +static int nt37703_bl_get_brightness(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	u16 brightness;
> +	int ret;
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	return brightness;
> +}
> +
> +static const struct backlight_ops nt37703_bl_ops = {
> +	.update_status = nt37703_bl_update_status,
> +	.get_brightness = nt37703_bl_get_brightness,
> +};
> +
> +static struct backlight_device *nt37703_create_backlight(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	const struct backlight_properties props = {
> +		.type = BACKLIGHT_RAW,
> +		.brightness = 2047,
> +		.max_brightness = 4095,
> +	};
> +
> +	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, &nt37703_bl_ops,
> +					      &props);
> +}
> +
> +static const struct drm_display_mode bronco_tianma_modes[] = {
> +	{
> +		.clock = (1080 + 16 + 4 + 16) * (2400 + 44 + 2 + 14) * 144 / 1000,
> +		.hdisplay = 1080,
> +		.hsync_start = 1080 + 16,
> +		.hsync_end = 1080 + 16 + 4,
> +		.htotal = 1080 + 16 + 4 + 16,
> +		.vdisplay = 2400,
> +		.vsync_start = 2400 + 44,
> +		.vsync_end = 2400 + 44 + 2,
> +		.vtotal = 2400 + 44 + 2 + 14,
> +	},
> +};
> +
> +static int bronco_tianma_v2_init_sequence(struct panel_info *pinfo)
> +{
> +	struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
> +
> +	nt37703_switch_page(&dsi_ctx, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x06);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x29);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x07);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x2b, 0x1b, 0x00, 0x32);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0x01, 0x02, 0x1b, 0x08, 0x77);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x20);
> +	mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, 1080 - 1);
> +	mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, 2400 - 1);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
> +	nt37703_cmd_test_on(&dsi_ctx);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x15);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x01, 0xe1);
> +	nt37703_cmd_test_on(&dsi_ctx);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x17);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x13);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x3c);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf5, 0x81);
> +	mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00);
> +	nt37703_switch_page(&dsi_ctx, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc7, 0x47);
> +	nt37703_switch_page(&dsi_ctx, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0xc4);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x09, 0x84, 0x09, 0x84, 0x09, 0x84, 0x16,
> +				     0xd6);
> +	nt37703_switch_page(&dsi_ctx, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x33);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc6, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00,
> +				     0x18);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x83);
> +	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8b, 0x00, 0x00);
> +	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
> +	mipi_dsi_msleep(&dsi_ctx, 120);
> +	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
> +
> +	return dsi_ctx.accum_err;
> +}
> +
> +static const struct drm_dsc_config bronco_tianma_dsc = {
> +	.dsc_version_major = 1,
> +	.dsc_version_minor = 1,
> +	.slice_height = 20,
> +	.slice_width = 540,
> +	.slice_count = 2,		/* 1080 / slice_width */
> +	.bits_per_component = 10,
> +	.bits_per_pixel = 8 << 4,	/* 4 fractional bits  */
> +	.block_pred_enable = true,
> +	.first_line_bpg_offset = 13,
> +};
> +
> +static const struct panel_desc bronco_tianma_v2_desc = {
> +	.modes = bronco_tianma_modes,
> +	.num_modes = ARRAY_SIZE(bronco_tianma_modes),
> +	.width_mm = 68,
> +	.height_mm = 152,
> +	.lanes = 4,
> +	.format = MIPI_DSI_FMT_RGB101010,
> +	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
> +		      MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM |
> +		      MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT;
> +	.init_sequence = bronco_tianma_v2_init_sequence,
> +	.dsc = &bronco_tianma_dsc,
> +};
> +
> +static int nt37703_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct panel_info *pinfo;
> +	int ret;
> +
> +	pinfo = devm_drm_panel_alloc(dev, struct panel_info, panel, &nt37703_panel_funcs,
> +				     DRM_MODE_CONNECTOR_DSI);
> +	if (IS_ERR(pinfo))
> +		return PTR_ERR(pinfo);
> +
> +	pinfo->desc = of_device_get_match_data(dev);
> +	if (IS_ERR(pinfo->desc))
> +		return PTR_ERR(pinfo->desc);
> +
> +	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(nt37703_supplies), nt37703_supplies,
> +					    &pinfo->supplies);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to get regulators\n");
> +
> +	pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(pinfo->reset_gpio))
> +		return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio),
> +				     "Failed to get reset-gpios\n");
> +
> +	pinfo->dsi = dsi;
> +	mipi_dsi_set_drvdata(dsi, pinfo);
> +
> +	dsi->lanes = pinfo->desc->lanes;
> +	dsi->format = pinfo->desc->format;
> +	dsi->mode_flags = pinfo->desc->mode_flags;
> +
> +	pinfo->panel.prepare_prev_first = true;
> +
> +	pinfo->panel.backlight = nt37703_create_backlight(dsi);
> +	if (IS_ERR(pinfo->panel.backlight))
> +		return dev_err_probe(dev, PTR_ERR(pinfo->panel.backlight),
> +				     "Failed to create backlight\n");
> +
> +	drm_panel_add(&pinfo->panel);

Use devm_ variant

> +
> +	/* This panel only supports DSC; unconditionally enable it */
> +	pinfo->dsc = *(pinfo->desc->dsc);
> +	dsi->dsc = &pinfo->dsc;
> +
> +	ret = mipi_dsi_attach(dsi);

Use devm_ variant

> +	if (ret < 0) {
> +		drm_panel_remove(&pinfo->panel);
> +		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static void nt37703_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct panel_info *pinfo = 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);
> +
> +	drm_panel_remove(&pinfo->panel);
> +}

And drop the remove()

Thanks.
Neil

> +
> +static const struct of_device_id nt37703_of_match[] = {
> +	{
> +		.compatible = "motorola,bronco-tianma-v2-nt37703",
> +		.data = &bronco_tianma_v2_desc,
> +	},
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, nt37703_of_match);
> +
> +static struct mipi_dsi_driver nt37703_driver = {
> +	.probe = nt37703_probe,
> +	.remove = nt37703_remove,
> +	.driver = {
> +		.name = "panel-novatek-nt37703",
> +		.of_match_table = nt37703_of_match,
> +	},
> +};
> +module_mipi_dsi_driver(nt37703_driver);
> +
> +MODULE_AUTHOR("Esteban Urrutia <[email protected]>");
> +MODULE_DESCRIPTION("DRM driver for Novatek NT37703 based MIPI DSI panels");
> +MODULE_LICENSE("GPL");
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.