[PATCH v2 2/3] drm/arm: Add support for Mali Display Processors

Emil Velikov emil.l.velikov at gmail.com
Tue May 24 00:01:33 UTC 2016


Hi Liviu,

Humble request: For the future please split things into manageable
hunks. I doubt you wrote the whole thing in one go, right ?

At the very minimum you could have introduced DP500 support initially
and then DP550 and DP650 as follow up commits.

On 25 April 2016 at 15:19, Liviu Dudau <Liviu.Dudau at arm.com> wrote:

> --- /dev/null
> +++ b/drivers/gpu/drm/arm/malidp_crtc.c
> @@ -0,0 +1,259 @@

> +static void malidp_crtc_enable(struct drm_crtc *crtc)
> +{
> +       struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
> +       struct malidp_hw_device *hwdev = malidp->dev;
> +       struct videomode vm;
> +
> +       drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
> +
> +       clk_prepare_enable(hwdev->pxlclk);
> +
> +       /* mclk needs to be set to the same or higher rate than pxlclk */
> +       clk_set_rate(hwdev->mclk, crtc->state->adjusted_mode.crtc_clock * 1000);
> +       clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);
> +
> +       hwdev->modeset(hwdev, &vm);
> +       hwdev->leave_config_mode(hwdev);
> +}
> +
> +static void malidp_crtc_disable(struct drm_crtc *crtc)
> +{
> +       struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
> +       struct malidp_hw_device *hwdev = malidp->dev;
> +
> +       /*
> +        * avoid disabling already disabled clocks and hardware
> +        * (as is the case at device probe time)
> +        */
Ideally one should lower the pxlclk, correct ?

> +       if (crtc->state->active) {
> +               hwdev->enter_config_mode(hwdev);
> +               clk_disable_unprepare(hwdev->pxlclk);
> +       }
> +}
> +


> +int malidp_crtc_init(struct drm_device *drm)
> +{
> +       struct malidp_drm *malidp = drm->dev_private;
> +       struct drm_plane *primary = NULL, *plane;
> +       int ret;
> +
You want malidp_de_planes_init() in here.

> +       drm_for_each_plane(plane, drm) {
> +               if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
> +                       primary = plane;
> +                       break;
> +               }
> +       }
> +
> +       if (!primary) {
> +               DRM_ERROR("no primary plane found\n");
... and _destroy() here.


> --- /dev/null
> +++ b/drivers/gpu/drm/arm/malidp_drv.c

> +static int malidp_init(struct drm_device *drm)
> +{
> +       int ret;
> +       struct malidp_drm *malidp = drm->dev_private;
> +       struct malidp_hw_device *hwdev = malidp->dev;
> +
> +       drm_mode_config_init(drm);
> +
> +       drm->mode_config.min_width = hwdev->min_line_size;
> +       drm->mode_config.min_height = hwdev->min_line_size;
> +       drm->mode_config.max_width = hwdev->max_line_size;
> +       drm->mode_config.max_height = hwdev->max_line_size;
> +       drm->mode_config.funcs = &malidp_mode_config_funcs;
> +
> +       ret = malidp_de_planes_init(drm);
Move this in malidp_crtc_init() ...

> +       if (ret < 0) {
> +               DRM_ERROR("Failed to initialise planes\n");
> +               goto plane_init_fail;
> +       }
> +
> +       ret = malidp_crtc_init(drm);
> +       if (ret) {
> +               DRM_ERROR("Failed to initialise CRTC\n");
> +               goto crtc_init_fail;
> +       }
> +
> +       return 0;
> +
> +crtc_init_fail:
> +       malidp_de_planes_destroy(drm);
... and drop this ?

> +plane_init_fail:
Nitpick: there is/was the idea that labels should be called after what
they do, rather than what fails. Personally I'm in favour of it as it
makes things clearer... sadly I might be one of the few.

> +       drm_mode_config_cleanup(drm);
> +
> +       return ret;
> +}
> +
Add a malidp_fini() helper to complement the above ?

> +static int malidp_irq_init(struct platform_device *pdev)

Ditto - add malidp_irq_fini() to complement the _init() function ?


> +static int malidp_bind(struct device *dev)
> +{

> +       /*
> +        * copy the associated data from malidp_drm_of_match to avoid
> +        * having to keep a reference to the OF node after binding
> +        */
This feels a bit strange. Is keeping a reference that bad ?



> --- /dev/null
> +++ b/drivers/gpu/drm/arm/malidp_hw.c

> +#define MALIDP_COMMON_FORMATS \
> +       /*    layers supporting the format,      internal id,      fourcc */ \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 0), DRM_FORMAT_ARGB2101010 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 1), DRM_FORMAT_ABGR2101010 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 2), DRM_FORMAT_RGBA1010102 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 3), DRM_FORMAT_BGRA1010102 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 0), DRM_FORMAT_ARGB8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 1), DRM_FORMAT_ABGR8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 2), DRM_FORMAT_RGBA8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 3), DRM_FORMAT_BGRA8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 0), DRM_FORMAT_XRGB8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 1), DRM_FORMAT_XBGR8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 2), DRM_FORMAT_RGBX8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 3), DRM_FORMAT_BGRX8888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 0), DRM_FORMAT_RGB888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 1), DRM_FORMAT_BGR888 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 0), DRM_FORMAT_RGBA5551 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1), DRM_FORMAT_ABGR1555 }, \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2), DRM_FORMAT_RGB565 },   \
> +       { DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3), DRM_FORMAT_BGR565 },   \
> +       { DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2), DRM_FORMAT_YUYV }, \
> +       { DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3), DRM_FORMAT_UYVY }, \
> +       { DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6), DRM_FORMAT_NV12 }, \
> +       { DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7), DRM_FORMAT_YUV420 }
> +
> +static const struct malidp_input_format malidp550_de_formats[] = {
> +       MALIDP_COMMON_FORMATS,
> +};
> +
> +static const struct malidp_input_format malidp650_de_formats[] = {
> +       MALIDP_COMMON_FORMATS,
> +};
> +
Pretty sure that using the define here will lead to the exact same
code existing twice in the driver. Just kill off malidp650_de_formats
and use malidp550_de_formats instead ?


> +void malidp500_enter_config_mode(struct malidp_hw_device *hwdev)
Many/most of the functions in this file should be static.

> +{
> +       u32 status, count = 100;
> +
Any particular reason behind the asymmetric (vs the leave_config_mode)
'count' ? Worth adding a comment ?

> +       malidp_hw_setbits(hwdev, MALIDP500_DC_CONFIG_REQ, MALIDP500_DC_CONTROL);
> +       while (count) {
> +               status = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_REG_STATUS);
> +               if ((status & MALIDP500_DC_CONFIG_REQ) == MALIDP500_DC_CONFIG_REQ)
> +                       break;
> +               /*
> +                * entering config mode can take as long as the rendering
> +                * of a full frame, hence the long sleep here
> +                */
> +               usleep_range(1000, 10000);
> +               count--;
> +       }
> +       WARN(count == 0, "timeout while entering config mode");
> +}
> +
> +void malidp500_leave_config_mode(struct malidp_hw_device *hwdev)
> +{
> +       u32 status, count = 30;
> +
...


> +u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
> +                          u8 layer_id, u32 format)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < map->n_input_formats; i++) {
> +               if (((map->input_formats[i].layer & layer_id) == layer_id) &&
> +                   (map->input_formats[i].format == format))
> +                       return map->input_formats[i].id;
> +       }
> +
> +       return (u8)-1;
This feels very strange to read. Use 0xff (here and below) instead ?

> +}
> +
> +
> +u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
> +{
> +       u32 value = readl(hwdev->regs + reg);
> +       return value;
> +}
> +
> +void malidp_hw_write(struct malidp_hw_device *hwdev, u32 value, u32 reg)
> +{
> +       writel(value, hwdev->regs + reg);
> +}
> +
> +void malidp_hw_setbits(struct malidp_hw_device *hwdev, u32 mask, u32 reg)
> +{
> +       u32 data = malidp_hw_read(hwdev, reg);
> +
> +       data |= mask;
> +       malidp_hw_write(hwdev, data, reg);
> +}
> +
> +void malidp_hw_clearbits(struct malidp_hw_device *hwdev, u32 mask, u32 reg)
> +{
> +       u32 data = malidp_hw_read(hwdev, reg);
> +
> +       data &= ~mask;
> +       malidp_hw_write(hwdev, data, reg);
> +}
> +
Just declare the above 4 as static inline ? Or the read/write ones at least.


> +void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq)
> +{
> +       u32 base = 0;
> +
> +       switch (block) {
> +       case MALIDP_DE_BLOCK:
> +               base = 0;
> +               break;
> +       case MALIDP_SE_BLOCK:
> +               base = hwdev->map.se_base;
> +               break;
> +       case MALIDP_DC_BLOCK:
> +               base = hwdev->map.dc_base;
> +               break;
> +       }
> +
Move the above switch into a helper function, instead of having three
copies of it ?


> +int malidp_de_irq_init(struct drm_device *drm, int irq)
> +{
> +       struct malidp_drm *malidp = drm->dev_private;
> +       struct malidp_hw_device *hwdev = malidp->dev;
> +       int ret;
> +
> +       /* ensure interrupts are disabled */
> +       malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff);
> +       malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff);
> +       malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff);
> +       malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff);
> +
Shouldn't we disable/clear DE before DC ? This way It aligns with
_cleanup and is the inverse of enable a couple of lines below.

> +       ret = devm_request_threaded_irq(drm->dev, irq, malidp_de_irq,
> +                                       malidp_de_irq_thread_handler,
> +                                       IRQF_SHARED, "malidp-de", drm);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to install DE IRQ handler\n");
> +               return ret;
> +       }
> +
> +       /* first enable the DC block IRQs */
> +       malidp_hw_enable_irq(hwdev, MALIDP_DC_BLOCK,
> +                            hwdev->map.dc_irq_map.irq_mask);
> +
> +       /* now enable the DE block IRQs */
> +       malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
> +                            hwdev->map.de_irq_map.irq_mask);
> +
> +       return 0;
> +}


> --- /dev/null
> +++ b/drivers/gpu/drm/arm/malidp_hw.h
> @@ -0,0 +1,189 @@

> +#include <drm/drm_fourcc.h>
Afaict the header is not used here. Please include it only where needed.


> +struct malidp_hw_device {
> +       const struct malidp_hw_regmap map;
> +       void __iomem *regs;
> +
> +       /* APB clock */
> +       struct clk *pclk;
> +       /* AXI clock */
> +       struct clk *aclk;
> +       /* main clock for display core */
> +       struct clk *mclk;
> +       /* pixel clock for display core */
> +       struct clk *pxlclk;
> +
> +       /*
> +        * Validate the driver instance against the hardware bits
> +        */
> +       int (*query_hw)(struct malidp_hw_device *hwdev);
> +
> +       /*
> +        * Set the hardware into config mode, ready to accept mode changes
> +        */
> +       void (*enter_config_mode)(struct malidp_hw_device *hwdev);
> +
> +       /*
> +        * Tell hardware to exit configuration mode
> +        */
> +       void (*leave_config_mode)(struct malidp_hw_device *hwdev);
> +
> +       /*
> +        * Query if hardware is in configuration mode
> +        */
> +       bool (*in_config_mode)(struct malidp_hw_device *hwdev);
> +
> +       /*
> +        * Set configuration valid flag for hardware parameters that can
> +        * be changed outside the configuration mode. Hardware will use
> +        * the new settings when config valid is set after the end of the
> +        * current buffer scanout
> +        */
> +       void (*set_config_valid)(struct malidp_hw_device *hwdev);
> +
> +       /*
> +        * Set a new mode in hardware. Requires the hardware to be in
> +        * configuration mode before this function is called.
> +        */
> +       void (*modeset)(struct malidp_hw_device *hwdev, struct videomode *m);
> +
> +       /*
> +        * Calculate the required rotation memory given the active area
> +        * and the buffer format.
> +        */
> +       int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
> +
Nitpick: We normally create use "const struct foo *funcs" for vfuncs.

Many of the structs in this file have holes in them. Worth checking
with pahole and reordering ?

> +/*
> + * background color components are defined as 12bits values,
> + * they will be shifted right when stored on hardware that
> + * supports only 8bits per channel
> + */
> +#define MALIDP_BGND_COLOR_R            0x000
> +#define MALIDP_BGND_COLOR_G            0x000
> +#define MALIDP_BGND_COLOR_B            0x000
> +
Something feels very wrong here. Are you sure what all three are zero ?


> --- /dev/null
> +++ b/drivers/gpu/drm/arm/malidp_planes.c

> +static int malidp_de_atomic_update_plane(struct drm_plane *plane,
> +                                        struct drm_crtc *crtc,
> +                                        struct drm_framebuffer *fb,
> +                                        int crtc_x, int crtc_y,
> +                                        unsigned int crtc_w,
> +                                        unsigned int crtc_h,
> +                                        uint32_t src_x, uint32_t src_y,
> +                                        uint32_t src_w, uint32_t src_h)
> +{
> +       return drm_atomic_helper_update_plane(plane, crtc, fb, crtc_x, crtc_y,
> +                                             crtc_w, crtc_h, src_x, src_y,
> +                                             src_w, src_h);
Just drop the wrapper and reference the helper directly into
drm_plane_funcs below ?


> +static void malidp_de_plane_update(struct drm_plane *plane,
> +                                  struct drm_plane_state *old_state)
> +{
> +       struct drm_gem_cma_object *obj;
> +       struct malidp_plane *mp;
> +       const struct malidp_hw_regmap *map;
> +       u8 format_id;
> +       u16 ptr;
> +       u32 format, src_w, src_h, dest_w, dest_h, val = 0;
> +       int num_planes, i;
> +
> +       mp = to_malidp_plane(plane);
> +
> +#ifdef MALIDP_ENABLE_BGND_COLOR_AS_PRIMARY_PLANE
> +       /* skip the primary plane, it is using the background color */
> +       if (!mp->layer || !mp->layer->id)
> +               return;
> +#endif
> +
Afaict the above define is not set anywhere - neither explicitly
(#define foo, -DFOO) nor implicitly (via kconfig toggle). As such it
should go. Same goes for the other instances of it.


> +       format_id = malidp_hw_get_format_id(map, mp->layer->id, format);
> +       if (format_id == (u8)-1)
> +               return;
> +
We should move this to from _update to _check.


> +int malidp_de_planes_init(struct drm_device *drm)
> +{
...
> +       for (i = 0; i < map->n_layers; i++) {
> +               u8 id = map->layers[i].id;
> +
> +               plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
Either use the non-devm function here and in _destroy or drop the one
in _destroy ? Afaict we could get a double-free with the latter in
place. Personally, I'd drop the devm_.


The best thing is that I did not spot even one use of deprecated
functions. Nicely done gents :-)

Regards,
Emil


More information about the dri-devel mailing list