[PATCH 20/60] drm/panel: Add driver for the NEC NL8048HL11 panel

Laurent Pinchart laurent.pinchart at ideasonboard.com
Thu Aug 8 15:17:20 UTC 2019


Hi Sam,

On Mon, Jul 08, 2019 at 09:10:08PM +0200, Sam Ravnborg wrote:
> Hi Laurent.
> 
> Thanks for keeping me busy :-)

My pleasure ;-)

> On Sun, Jul 07, 2019 at 09:18:57PM +0300, Laurent Pinchart wrote:
> > This panel is used on the Zoom2/3/3630 SDP boards.
> 
> This information may be good to have in the Kconfig help entry too.
> 
> Maybe tell in the changelog where this code originates from.

Good point, I'll do so.

> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
> > @@ -0,0 +1,249 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * NEC NL8048HL11 Panel Driver
> > + *
> > + * Copyright (C) 2019 Texas Instruments Incorporated
> > + *
> > + * Based on the omapdrm-specific panel-nec-nl8048hl11 driver
> > + *
> > + * Copyright (C) 2010 Texas Instruments Incorporated
> > + * Author: Erik Gilling <konkers at android.com>
> > + */
>
> No added copyright from you?
> (Apply to all panel drivers)

The copyright goes to TI for this specific work.

> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +#include <linux/pm.h>
> > +#include <linux/spi/spi.h>
> > +
> > +#include <drm/drm_connector.h>
> > +#include <drm/drm_modes.h>
> > +#include <drm/drm_panel.h>
> 
> Good to see panel drivers that are NOT using drmP.h :-)
> 
> > +
> > +struct nl8048_device {
> > +	struct drm_panel panel;
> > +
> > +	struct spi_device *spi;
> > +	struct gpio_desc *reset_gpio;
> > +};
>
> Naming bikeshedding. This is a nl8048_panel, not a device.

It's a device too, but I'll change this.

> > +
> > +static const struct drm_display_mode nl8048_mode = {
> > +	/*  NEC PIX Clock Ratings MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz */
> > +	.clock	= 23800,
> > +	.hdisplay = 800,
> > +	.hsync_start = 800 + 6,
> > +	.hsync_end = 800 + 6 + 1,
> > +	.htotal = 800 + 6 + 1 + 4,
> > +	.vdisplay = 480,
> > +	.vsync_start = 480 + 3,
> > +	.vsync_end = 480 + 3 + 1,
> > +	.vtotal = 480 + 3 + 1 + 4,
> > +	.vrefresh = 60,
> > +	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> > +	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
> > +};
>
> Same comment as for previous patch on height/width.

Fixed.

> > +
> > +static int nl8048_get_modes(struct drm_panel *panel)
> > +{
> > +	struct drm_connector *connector = panel->connector;
> > +	struct drm_display_mode *mode;
> > +
> > +	mode = drm_mode_duplicate(panel->drm, &nl8048_mode);
> > +	if (!mode)
> > +		return -ENOMEM;
> > +
> > +	drm_mode_set_name(mode);
> > +	drm_mode_probed_add(connector, mode);
> > +
> > +	connector->display_info.width_mm = 89;
> > +	connector->display_info.height_mm = 53;
> > +	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
> > +					  | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
> > +					  | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
> > +
> > +	return 1;
> > +}
> 
> This should be moved to drm_panle_helper.c (which we do not have yet.
> But the function is used in some many drivers it makes sense.
> On my TODO list.
> I have yet to find a good way to specify the bus_flags though.

It's a bit annoying to have different set of flags for the various
structures that describe modes and display information. Cleaning all
that up would be great, but will be a significant effort.

> > +#ifdef CONFIG_PM_SLEEP
> 
> Use __maybe_unused, and loose the #ifdef

OK.

> And why does this panel need suspend/resume?
> The panel is supposed to be turned off in disable()
> 
> To simple solution would be to move the write to "2" to
> the enable and disable functions.
> And then this driver is alinged with the rest.
> 
> > +static int nl8048_suspend(struct device *dev)
> > +{
> > +	struct nl8048_device *lcd = dev_get_drvdata(dev);
> > +
> > +	nl8048_write(lcd, 2, 0x01);
> > +	msleep(40);
> 
> This sleep puzzle me. What is the puspose?
> And the write is to a display that is already reset??

This has been copied from the omapdrm-specific driver, and I have no
hardware to test this, so I didn't dare changing the code. I would
prefer reworking the suspend/resume on top of this patch, ideally
developed by someone who can test the changes.

> > +
> > +	return 0;
> > +}
> > +
> > +static int nl8048_resume(struct device *dev)
> > +{
> > +	struct nl8048_device *lcd = dev_get_drvdata(dev);
> > +
> > +	/* Reinitialize the panel. */
> > +	spi_setup(lcd->spi);
> > +	nl8048_write(lcd, 2, 0x00);
> > +	nl8048_init(lcd);
> > +
> > +	return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(nl8048_pm_ops, nl8048_suspend, nl8048_resume);
> > +#endif
> > +
> > +static int nl8048_probe(struct spi_device *spi)
> > +{
> > +	struct nl8048_device *lcd;
> > +	int ret;
> > +
> > +	lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
> > +	if (lcd == NULL)
> > +		return -ENOMEM;
> > +
> > +	spi_set_drvdata(spi, lcd);
> > +	lcd->spi = spi;
> > +
> > +	lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
> > +	if (IS_ERR(lcd->reset_gpio)) {
> > +		dev_err(&spi->dev, "failed to parse reset gpio\n");
> > +		return PTR_ERR(lcd->reset_gpio);
> > +	}
> > +
> > +	spi->mode = SPI_MODE_0;
> > +	spi->bits_per_word = 32;
> > +
> > +	ret = spi_setup(spi);
> > +	if (ret < 0) {
> > +		dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = nl8048_init(lcd);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	drm_panel_init(&lcd->panel);
> > +	lcd->panel.dev = &lcd->spi->dev;
> > +	lcd->panel.funcs = &nl8048_funcs;
> > +
> > +	return drm_panel_add(&lcd->panel);
> > +}
> > +
> > +static int nl8048_remove(struct spi_device *spi)
> > +{
> > +	struct nl8048_device *lcd = spi_get_drvdata(spi);
> > +
> > +	drm_panel_remove(&lcd->panel);
> > +	nl8048_disable(&lcd->panel);
> 
> Use drm_panel_disable() - same comment as other panel driver.

Sure.

> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id nl8048_of_match[] = {
> > +	{ .compatible = "nec,nl8048hl11", },
> > +	{},
> 
> { /* sentinel */ },?

Done.

> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, nl8048_of_match);
> > +
> > +static struct spi_driver nl8048_driver = {
> > +	.probe		= nl8048_probe,
> > +	.remove		= nl8048_remove,
> > +	.driver		= {
> > +		.name	= "panel-nec-nl8048hl11",
> > +#ifdef CONFIG_PM_SLEEP
> > +		.pm	= &nl8048_pm_ops,
> > +#endif
> > +		.of_match_table = nl8048_of_match,
> > +	},
> > +};
> > +
> > +module_spi_driver(nl8048_driver);
> > +
> > +MODULE_ALIAS("spi:nec,nl8048hl11");
> > +MODULE_AUTHOR("Erik Gilling <konkers at android.com>");
> > +MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
> > +MODULE_LICENSE("GPL");
> "GPL v2"?
> 
> The suspend/resume thing needs to be sorted out before I can add my r-b
> on this.

-- 
Regards,

Laurent Pinchart


More information about the dri-devel mailing list