[PATCH 23/60] drm/panel: Add driver for the Toppology TD028TTEC1 panel

Laurent Pinchart laurent.pinchart at ideasonboard.com
Thu Aug 8 15:43:50 UTC 2019


Hi Sam,

On Wed, Jul 10, 2019 at 09:48:55AM +0200, Sam Ravnborg wrote:
> Hi Laurent.
> 
> This driver looks very good.
> 
> On Sun, Jul 07, 2019 at 09:19:00PM +0300, Laurent Pinchart wrote:
> > This panel is used on the OpenMoko Neo FreeRunner and Neo 1973.
> 
> Add info in Kconfig help entry?
> 
> > +config DRM_PANEL_TPO_TD028TTEC1
> > +	tristate "TPO TD028TTEC1 panel driver"
> 
> Maybe spell out TPO like "TPO (Topology) TD028..."
>
> > +	depends on OF && SPI
> > +	depends on BACKLIGHT_CLASS_DEVICE
> > +	help
> > +	  Say Y here if you want to enable support for TPO TD028TTEC1 480x640
> > +	  2.8" panel.
> > +
> >  config DRM_PANEL_TPO_TPG110
> >  	tristate "TPO TPG 800x400 panel"
> >  	depends on OF && SPI && GPIOLIB
> >  obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
> > diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
> > new file mode 100644
> > index 000000000000..05af9ea6339c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
> > +
> > +static int jbt_ret_write_0(struct td028ttec1_device *lcd, u8 reg, int *err)
> > +{
> > +	struct spi_device *spi = lcd->spi;
> > +	u16 tx_buf = JBT_COMMAND | reg;
> > +	int ret;
> > +
> > +	if (err && *err)
> > +		return *err;
> > +
> > +	ret = spi_write(spi, (u8 *)&tx_buf, sizeof(tx_buf));
> > +	if (ret < 0) {
> > +		dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
> > +		if (err)
> > +			*err = ret;
> > +	}
> > +
> > +	return ret;
> > +}
> 
> I like the way *err is used here.
> So if one call fails, the remaining calls are ignored.

I think it's a construct that could be used in more drivers, when
dealing with large sequences of writes.

> The way the code is written above it will only work on a little endian
> box, as the values are stored in an u16 that is later seen as an array of
> bytes.
> This is also true for the remaing similar functions and may be OK.
> We do not see any real demands for big endian anyway.

This could be fixed if desired, but I would do so on top of this patch
to minimise the changes compared to the omapdrm-specific panel driver.

> > +static int td028ttec1_enable(struct drm_panel *panel)
> > +{
> > +	struct td028ttec1_device *lcd = to_td028ttec1_device(panel);
> > +	unsigned int i;
> > +	int ret = 0;
> > +
> > +	/* Three times command zero */
> > +	for (i = 0; i < 3; ++i) {
> > +		jbt_ret_write_0(lcd, 0x00, &ret);
> > +		usleep_range(1000, 2000);
> > +	}
> > +
> > +	if (ret)
> > +		return ret;
> 
> This if (ret) is really not needed.
> It somehow short-circuit the principle used in the rest of the function
> here. All jbt_reg_write() will be nop if ret != 0.

I'll drop it.

> > +
> > +	/* deep standby out */
> > +	jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x17, &ret);
> > +
> > +	/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
> > +	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE, 0x80, &ret);
> > +
> > +	/* Quad mode off */
> > +	jbt_reg_write_1(lcd, JBT_REG_QUAD_RATE, 0x00, &ret);
> > +
> > +	/* AVDD on, XVDD on */
> > +	jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x16, &ret);
> > +
> > +	/* Output control */
> > +	jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0xfff9, &ret);
> > +
> > +	/* Sleep mode off */
> > +	jbt_ret_write_0(lcd, JBT_REG_SLEEP_OUT, &ret);
> > +
> > +	/* at this point we have like 50% grey */
> > +
> > +	/* initialize register set */
> > +	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE1, 0x01, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE2, 0x00, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_RGB_FORMAT, 0x60, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_DRIVE_SYSTEM, 0x10, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_OP, 0x56, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_MODE, 0x33, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_OPAMP_SYSCLK, 0x02, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_VSC_VOLTAGE, 0x2b, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_VCOM_VOLTAGE, 0x40, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_EXT_DISPL, 0x03, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_DCCLK_DCEV, 0x04, &ret);
> > +	/*
> > +	 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
> > +	 * to avoid red / blue flicker
> > +	 */
> > +	jbt_reg_write_1(lcd, JBT_REG_ASW_SLEW, 0x04, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_DUMMY_DISPLAY, 0x00, &ret);
> > +
> > +	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_A, 0x11, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_B, 0x11, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_C, 0x11, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0, &ret);
> > +
> > +	jbt_reg_write_2(lcd, JBT_REG_GAMMA1_FINE_1, 0x5533, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_FINE_2, 0x00, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_INCLINATION, 0x00, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00, &ret);
> > +
> > +	jbt_reg_write_2(lcd, JBT_REG_HCLOCK_VGA, 0x1f0, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_BLANK_CONTROL, 0x02, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_BLANK_TH_TV, 0x0804, &ret);
> > +
> > +	jbt_reg_write_1(lcd, JBT_REG_CKV_ON_OFF, 0x01, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_CKV_1_2, 0x0000, &ret);
> > +
> > +	jbt_reg_write_2(lcd, JBT_REG_OEV_TIMING, 0x0d0e, &ret);
> > +	jbt_reg_write_2(lcd, JBT_REG_ASW_TIMING_1, 0x11a4, &ret);
> > +	jbt_reg_write_1(lcd, JBT_REG_ASW_TIMING_2, 0x0e, &ret);
> > +
> > +	jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, &ret);
> > +
> > +	if (ret)
> > +		return ret;
> > +
> > +	backlight_enable(lcd->backlight);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct drm_display_mode td028ttec1_mode = {
> > +	.clock = 22153,
> > +	.hdisplay = 480,
> > +	.hsync_start = 480 + 24,
> > +	.hsync_end = 480 + 24 + 8,
> > +	.htotal = 480 + 24 + 8 + 8,
> > +	.vdisplay = 640,
> > +	.vsync_start = 640 + 4,
> > +	.vsync_end = 640 + 4 + 2,
> > +	.vtotal = 640 + 4 + 2 + 2,
> > +	.vrefresh = 66,
> > +	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> > +	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
> > +};
> 
> Add width_mm + height_mm.
> 
> > +static int td028ttec1_remove(struct spi_device *spi)
> > +{
> > +	struct td028ttec1_device *lcd = spi_get_drvdata(spi);
> > +
> > +	drm_panel_remove(&lcd->panel);
> > +	td028ttec1_disable(&lcd->panel);
> 
> Use drm_panel_disable();
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id td028ttec1_of_match[] = {
> > +	{ .compatible = "tpo,td028ttec1", },
> > +	/* DT backward compatibility. */
> > +	{ .compatible = "toppoly,td028ttec1", },
> > +	{},
> 
> { /* sentinel */ },
> 
> With the above nits fixed/considered:
> Reviewed-by: Sam Ravnborg <sam at ravnborg.org>
> 
> 	Sam

-- 
Regards,

Laurent Pinchart


More information about the dri-devel mailing list