[PATCH v2 7/7] drm: Renesas SH Mobile DRM driver

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Aug 6 06:12:36 PDT 2012


Hi Sascha,

Thanks for the review.

> On Fri, Jul 20, 2012 at 03:04:22PM +0200, Laurent Pinchart wrote:
> > The SH Mobile LCD controller (LCDC) DRM driver supports the main
> > graphics plane in RGB and YUV formats, as well as the overlay planes (in
> > alpha-blending mode only).
> > 
> > Only flat panel outputs using the parallel interface are supported.
> > Support for SYS panels, HDMI and DSI is currently not implemented.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

[snip]

> > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> > b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c new file mode 100644
> > index 0000000..c7be5f7
> > --- /dev/null
> > +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> > @@ -0,0 +1,768 @@
> 
> [...]
> 
> > +
> > +static void shmob_drm_encoder_destroy(struct drm_encoder *encoder)
> > +{
> > +	drm_encoder_cleanup(encoder);
> > +}
> > +
> > +static const struct drm_encoder_funcs encoder_funcs = {
> > +	.destroy = shmob_drm_encoder_destroy,
> 
> You could add drm_encoder_cleanup here.

Indeed, will fix.

> [...]
> 
> > +
> > +static enum drm_connector_status
> > +shmob_drm_connector_detect(struct drm_connector *connector, bool force)
> > +{
> > +	return connector_status_unknown;
> > +}
> 
> Shouldn't you return connector_status_connected here? I mean all that
> you handle is a dumb display which is always connected.

OK.

> [...]
> 
> > +
> > +static int __devinit shmob_drm_setup_clocks(struct shmob_drm_device
> > *sdev,
> > +					    enum shmob_drm_clk_source clksrc)
> > +{
> > +	struct clk *clk;
> > +	char *clkname;
> > +
> > +	switch (clksrc) {
> > +	case SHMOB_DRM_CLK_BUS:
> > +		clkname = "bus_clk";
> > +		sdev->lddckr = LDDCKR_ICKSEL_BUS;
> > +		break;
> > +	case SHMOB_DRM_CLK_PERIPHERAL:
> > +		clkname = "peripheral_clk";
> > +		sdev->lddckr = LDDCKR_ICKSEL_MIPI;
> > +		break;
> > +	case SHMOB_DRM_CLK_EXTERNAL:
> > +		clkname = NULL;
> > +		sdev->lddckr = LDDCKR_ICKSEL_HDMI;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	clk = clk_get(sdev->dev, clkname);
> > +	if (IS_ERR(clk)) {
> > +		dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
> > +		return PTR_ERR(clk);
> > +	}
> > +
> > +	sdev->clock = clk;
> > +	return 0;
> > +}
> > +
> > +/*
> > -------------------------------------------------------------------------
> > ---- + * DRM operations
> > + */
> > +
> > +static int shmob_drm_unload(struct drm_device *dev)
> > +{
> > +	struct shmob_drm_device *sdev = dev->dev_private;
> > +
> > +	drm_kms_helper_poll_fini(dev);
> > +	drm_vblank_cleanup(dev);
> > +	drm_irq_uninstall(dev);
> > +
> > +	if (sdev->clock)
> > +		clk_put(sdev->clock);
> 
> When the clk_get above failed sdev->clock may be a error pointer here which
> you clk_put.

If clk_get() returns an error pointer sdev->clock is not assigned:

        clk = clk_get(sdev->dev, clkname);
        if (IS_ERR(clk)) {
                dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
                return PTR_ERR(clk);
        }

        sdev->clock = clk;

so sdev->clock should stay NULL in that case.

> > +
> > +	if (sdev->mmio)
> > +		iounmap(sdev->mmio);
> > +
> > +	dev->dev_private = NULL;
> > +	kfree(sdev);
> > +
> > +	return 0;
> > +}
> > +
> > +static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
> > +{
> > +	struct shmob_drm_platform_data *pdata = dev->dev->platform_data;
> > +	struct platform_device *pdev = dev->platformdev;
> > +	struct shmob_drm_device *sdev;
> > +	struct resource *res;
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	if (pdata == NULL) {
> > +		dev_err(dev->dev, "no platform data\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
> > +	if (sdev == NULL) {
> > +		dev_err(dev->dev, "failed to allocate private data\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	sdev->dev = &pdev->dev;
> > +	sdev->pdata = pdata;
> > +	spin_lock_init(&sdev->irq_lock);
> > +
> > +	sdev->ddev = dev;
> > +	dev->dev_private = sdev;
> > +
> > +	/* I/O resources and clocks */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (res == NULL) {
> > +		dev_err(&pdev->dev, "failed to get memory resource\n");
> > +		ret = -EINVAL;
> > +		goto done;
> > +	}
> > +
> > +	sdev->mmio = ioremap_nocache(res->start, resource_size(res));
> > +	if (sdev->mmio == NULL) {
> > +		dev_err(&pdev->dev, "failed to remap memory resource\n");
> > +		ret = -ENOMEM;
> > +		goto done;
> > +	}
> > +
> > +	ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
> > +	if (ret < 0)
> > +		goto done;
> > +
> > +	ret = shmob_drm_init_interface(sdev);
> > +	if (ret < 0)
> > +		goto done;
> > +
> > +	ret = shmob_drm_modeset_init(sdev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to initialize mode setting\n");
> > +		goto done;
> > +	}
> > +
> > +	for (i = 0; i < 4; ++i) {
> > +		ret = shmob_drm_plane_create(sdev, i);
> 
> I think you are loosing the resources allocated from
> shmob_drm_plane_create in case of an error below.

shmob_drm_unload() is called in the error path of shmob_drm_load(). However, 
it's missing a call to drm_mode_config_cleanup(). I'll add it.

> > +		if (ret < 0) {
> > +			dev_err(&pdev->dev, "failed to create plane %u\n", i);
> > +			goto done;
> > +		}
> > +	}
> > +
> > +	ret = drm_vblank_init(dev, 1);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to initialize vblank\n");
> > +		goto done;
> > +	}
> > +
> > +	ret = drm_irq_install(dev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to install IRQ handler\n");
> > +		goto done;
> > +	}
> > +
> > +done:
> > +	if (ret)
> > +		shmob_drm_unload(dev);
> > +
> > +	return ret;
> > +}
> > +
> 
> [...]
> 
> > +
> > +struct shmob_drm_device {
> > +	struct device *dev;
> > +	const struct shmob_drm_platform_data *pdata;
> > +
> > +	void __iomem *mmio;
> > +	struct clk *clock;
> > +	struct sh_mobile_meram_info *meram;
> > +	u32 lddckr;
> > +	u32 ldmt1r;
> > +
> > +	spinlock_t irq_lock;		/* Protects hardware LDINTR register */
> > +
> > +	struct drm_device *ddev;
> > +
> > +	struct shmob_drm_crtc crtc;
> > +	struct shmob_drm_encoder encoder;
> > +	struct shmob_drm_connector connector;
> > +
> > +	void *dma_mem;
> > +	unsigned long dma_size;
> > +	dma_addr_t dma_handle;
> 
> These three variables are unused.

Oops, good catch.

> > +};
> > +
> > +#endif /* __SHMOB_DRM_DRV_H__ */
> > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c
> > b/drivers/gpu/drm/shmobile/shmob_drm_kms.c new file mode 100644
> > index 0000000..c291ee3
> > --- /dev/null
> > +++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.c
> 
> [...]
> 
> > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> > b/drivers/gpu/drm/shmobile/shmob_drm_plane.c new file mode 100644
> > index 0000000..d22644d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> > @@ -0,0 +1,263 @@
> > +/*
> > + * shmob_drm_crtc.c  --  SH Mobile DRM CRTCs
> 
> s/shmob_drm_crtc.c/shmob_drm_plane.c/ and something about planes in the
> comment?

Sure :-)

> > + *
> > + * Copyright (C) 2012 Renesas Corporation
> > + *
> > + * Laurent Pinchart (laurent.pinchart at ideasonboard.com)
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> 
> [...]
> 
> > +
> > +int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int
> > index) +{
> > +	struct shmob_drm_plane *splane;
> > +
> > +	splane = kzalloc(sizeof(*splane), GFP_KERNEL);
> > +	if (splane == NULL)
> > +		return -ENOMEM;
> > +
> > +	splane->index = index;
> > +	splane->alpha = 255;
> > +
> > +	return drm_plane_init(sdev->ddev, &splane->plane, 1,
> > +			      &shmob_drm_plane_funcs, formats,
> > +			      ARRAY_SIZE(formats), false);
> 
> Shouldn't splane be freed if drm_plane_init fails?

It definitely should.

> > +}
> > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.h
> > b/drivers/gpu/drm/shmobile/shmob_drm_plane.h new file mode 100644
> > index 0000000..99623d0
> > --- /dev/null
> > +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.h
> > @@ -0,0 +1,22 @@
> > +/*
> > + * shmob_drm_plane.h  --  SH Mobile DRM Planes
> > + *
> > + * Copyright (C) 2012 Renesas Corporation
> > + *
> > + * Laurent Pinchart (laurent.pinchart at ideasonboard.com)
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> > +#ifndef __SHMOB_DRM_PLANE_H__
> > +#define __SHMOB_DRM_PLANE_H__
> > +
> > +struct shmob_drm_device;
> > +
> > +int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int
> > index); +void shmob_drm_plane_setup(struct drm_plane *plane);
> > +
> > +#endif /* __SHMOB_DRM_PLANE_H__ */
> > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_regs.h
> > b/drivers/gpu/drm/shmobile/shmob_drm_regs.h new file mode 100644
> > index 0000000..a90517e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/shmobile/shmob_drm_regs.h
> > @@ -0,0 +1,304 @@
> > +/*
> > + * shmob_drm_regs.g  --  SH Mobile DRM registers
> 
> s/shmob_drm_regs.g/shmob_drm_regs.h/

Fixed.

> > + *
> > + * Copyright (C) 2012 Renesas Corporation
> > + *
> > + * Laurent Pinchart (laurent.pinchart at ideasonboard.com)
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> 
> [...]
> 
> > +
> > +static inline void lcdc_write_mirror(struct shmob_drm_device *sdev, u32
> > reg, +				     u32 data)
> > +{
> > +	iowrite32(data, sdev->mmio + reg + LCDC_MIRROR_OFFSET);
> > +}
> > +
> > +static inline void lcdc_write(struct shmob_drm_device *sdev, u32 reg, u32
> > data) +{
> > +	iowrite32(data, sdev->mmio + reg);
> > +	if (lcdc_is_banked(reg))
> > +		iowrite32(data, sdev->mmio + reg + LCDC_SIDE_B_OFFSET);
> > +}
> > +
> > +static inline u32 lcdc_read(struct shmob_drm_device *sdev, u32 reg)
> > +{
> > +	return ioread32(sdev->mmio + reg);
> > +}
> > +
> > +static inline void lcdc_wait_bit(struct shmob_drm_device *sdev, u32 reg,
> > +				 u32 mask, u32 until)
> > +{
> > +	while ((lcdc_read(sdev, reg) & mask) != until)
> > +		cpu_relax();
> 
> Add a timeout here?

Good idea.

> > +}
> > +
> > +#endif /* __SHMOB_DRM_REGS_H__ */

-- 
Regards,

Laurent Pinchart


More information about the dri-devel mailing list