Commit ad49f860 authored by Liviu Dudau's avatar Liviu Dudau

drm/arm: Add support for Mali Display Processors

Add support for the new family of Display Processors from ARM Ltd.
This commit adds basic support for Mali DP500, DP550 and DP650
parts, with only the display engine being supported at the moment.

Cc: David Brown <David.Brown@arm.com>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Signed-off-by: default avatarLiviu Dudau <Liviu.Dudau@arm.com>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent ee6ea993
......@@ -25,3 +25,19 @@ config DRM_HDLCD_SHOW_UNDERRUN
Enable this option to show in red colour the pixels that the
HDLCD device did not fetch from framebuffer due to underrun
conditions.
config DRM_MALI_DISPLAY
tristate "ARM Mali Display Processor"
depends on DRM && OF && (ARM || ARM64)
depends on COMMON_CLK
select DRM_ARM
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
select VIDEOMODE_HELPERS
help
Choose this option if you want to compile the ARM Mali Display
Processor driver. It supports the DP500, DP550 and DP650 variants
of the hardware.
If compiled as a module it will be called mali-dp.
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
obj-$(CONFIG_DRM_HDLCD) += hdlcd.o
mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Liviu Dudau <Liviu.Dudau@arm.com>
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* ARM Mali DP500/DP550/DP650 driver (crtc operations)
*/
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <linux/clk.h>
#include <video/videomode.h>
#include "malidp_drv.h"
#include "malidp_hw.h"
static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
struct malidp_hw_device *hwdev = malidp->dev;
/*
* check that the hardware can drive the required clock rate,
* but skip the check if the clock is meant to be disabled (req_rate = 0)
*/
long rate, req_rate = mode->crtc_clock * 1000;
if (req_rate) {
rate = clk_round_rate(hwdev->mclk, req_rate);
if (rate < req_rate) {
DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
mode->crtc_clock);
return false;
}
rate = clk_round_rate(hwdev->pxlclk, req_rate);
if (rate != req_rate) {
DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
req_rate);
return false;
}
}
return true;
}
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);
drm_crtc_vblank_on(crtc);
}
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;
drm_crtc_vblank_off(crtc);
hwdev->enter_config_mode(hwdev);
clk_disable_unprepare(hwdev->pxlclk);
}
static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
struct malidp_hw_device *hwdev = malidp->dev;
struct drm_plane *plane;
const struct drm_plane_state *pstate;
u32 rot_mem_free, rot_mem_usable;
int rotated_planes = 0;
/*
* check if there is enough rotation memory available for planes
* that need 90° and 270° rotation. Each plane has set its required
* memory size in the ->plane_check() callback, here we only make
* sure that the sums are less that the total usable memory.
*
* The rotation memory allocation algorithm (for each plane):
* a. If no more rotated planes exist, all remaining rotate
* memory in the bank is available for use by the plane.
* b. If other rotated planes exist, and plane's layer ID is
* DE_VIDEO1, it can use all the memory from first bank if
* secondary rotation memory bank is available, otherwise it can
* use up to half the bank's memory.
* c. If other rotated planes exist, and plane's layer ID is not
* DE_VIDEO1, it can use half of the available memory
*
* Note: this algorithm assumes that the order in which the planes are
* checked always has DE_VIDEO1 plane first in the list if it is
* rotated. Because that is how we create the planes in the first
* place, under current DRM version things work, but if ever the order
* in which drm_atomic_crtc_state_for_each_plane() iterates over planes
* changes, we need to pre-sort the planes before validation.
*/
/* first count the number of rotated planes */
drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
if (pstate->rotation & MALIDP_ROTATED_MASK)
rotated_planes++;
}
rot_mem_free = hwdev->rotation_memory[0];
/*
* if we have more than 1 plane using rotation memory, use the second
* block of rotation memory as well
*/
if (rotated_planes > 1)
rot_mem_free += hwdev->rotation_memory[1];
/* now validate the rotation memory requirements */
drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
struct malidp_plane *mp = to_malidp_plane(plane);
struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
if (pstate->rotation & MALIDP_ROTATED_MASK) {
/* process current plane */
rotated_planes--;
if (!rotated_planes) {
/* no more rotated planes, we can use what's left */
rot_mem_usable = rot_mem_free;
} else {
if ((mp->layer->id != DE_VIDEO1) ||
(hwdev->rotation_memory[1] == 0))
rot_mem_usable = rot_mem_free / 2;
else
rot_mem_usable = hwdev->rotation_memory[0];
}
rot_mem_free -= rot_mem_usable;
if (ms->rotmem_size > rot_mem_usable)
return -EINVAL;
}
}
return 0;
}
static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
.mode_fixup = malidp_crtc_mode_fixup,
.enable = malidp_crtc_enable,
.disable = malidp_crtc_disable,
.atomic_check = malidp_crtc_atomic_check,
};
static const struct drm_crtc_funcs malidp_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
int malidp_crtc_init(struct drm_device *drm)
{
struct malidp_drm *malidp = drm->dev_private;
struct drm_plane *primary = NULL, *plane;
int ret;
ret = malidp_de_planes_init(drm);
if (ret < 0) {
DRM_ERROR("Failed to initialise planes\n");
return ret;
}
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");
ret = -EINVAL;
goto crtc_cleanup_planes;
}
ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
&malidp_crtc_funcs, NULL);
if (!ret) {
drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
return 0;
}
crtc_cleanup_planes:
malidp_de_planes_destroy(drm);
return ret;
}
This diff is collapsed.
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Liviu Dudau <Liviu.Dudau@arm.com>
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* ARM Mali DP500/DP550/DP650 KMS/DRM driver structures
*/
#ifndef __MALIDP_DRV_H__
#define __MALIDP_DRV_H__
#include <linux/mutex.h>
#include <linux/wait.h>
#include "malidp_hw.h"
struct malidp_drm {
struct malidp_hw_device *dev;
struct drm_fbdev_cma *fbdev;
struct list_head event_list;
struct drm_crtc crtc;
wait_queue_head_t wq;
atomic_t config_valid;
};
#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
struct malidp_plane {
struct drm_plane base;
struct malidp_hw_device *hwdev;
const struct malidp_layer *layer;
};
struct malidp_plane_state {
struct drm_plane_state base;
/* size of the required rotation memory if plane is rotated */
u32 rotmem_size;
};
#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)
int malidp_de_planes_init(struct drm_device *drm);
void malidp_de_planes_destroy(struct drm_device *drm);
int malidp_crtc_init(struct drm_device *drm);
/* often used combination of rotational bits */
#define MALIDP_ROTATED_MASK (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))
#endif /* __MALIDP_DRV_H__ */
This diff is collapsed.
/*
*
* (C) COPYRIGHT 2013-2016 ARM Limited. All rights reserved.
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* ARM Mali DP hardware manipulation routines.
*/
#ifndef __MALIDP_HW_H__
#define __MALIDP_HW_H__
#include <linux/bitops.h>
#include "malidp_regs.h"
struct videomode;
struct clk;
/* Mali DP IP blocks */
enum {
MALIDP_DE_BLOCK = 0,
MALIDP_SE_BLOCK,
MALIDP_DC_BLOCK
};
/* Mali DP layer IDs */
enum {
DE_VIDEO1 = BIT(0),
DE_GRAPHICS1 = BIT(1),
DE_GRAPHICS2 = BIT(2), /* used only in DP500 */
DE_VIDEO2 = BIT(3),
DE_SMART = BIT(4),
};
struct malidp_input_format {
u32 format; /* DRM fourcc */
u8 layer; /* bitmask of layers supporting it */
u8 id; /* used internally */
};
#define MALIDP_INVALID_FORMAT_ID 0xff
/*
* hide the differences between register maps
* by using a common structure to hold the
* base register offsets
*/
struct malidp_irq_map {
u32 irq_mask; /* mask of IRQs that can be enabled in the block */
u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */
};
struct malidp_layer {
u16 id; /* layer ID */
u16 base; /* address offset for the register bank */
u16 ptr; /* address offset for the pointer register */
};
/* regmap features */
#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0)
struct malidp_hw_regmap {
/* address offset of the DE register bank */
/* is always 0x0000 */
/* address offset of the SE registers bank */
const u16 se_base;
/* address offset of the DC registers bank */
const u16 dc_base;
/* address offset for the output depth register */
const u16 out_depth_base;
/* bitmap with register map features */
const u8 features;
/* list of supported layers */
const u8 n_layers;
const struct malidp_layer *layers;
const struct malidp_irq_map de_irq_map;
const struct malidp_irq_map se_irq_map;
const struct malidp_irq_map dc_irq_map;
/* list of supported input formats for each layer */
const struct malidp_input_format *input_formats;
const u8 n_input_formats;
};
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);
u8 features;
u8 min_line_size;
u16 max_line_size;
/* size of memory used for rotating layers, up to two banks available */
u32 rotation_memory[2];
};
/* Supported variants of the hardware */
enum {
MALIDP_500 = 0,
MALIDP_550,
MALIDP_650,
/* keep the next entry last */
MALIDP_MAX_DEVICES
};
extern const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES];
static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
{
return readl(hwdev->regs + reg);
}
static inline void malidp_hw_write(struct malidp_hw_device *hwdev,
u32 value, u32 reg)
{
writel(value, hwdev->regs + reg);
}
static inline 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);
}
static inline 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);
}
static inline u32 malidp_get_block_base(struct malidp_hw_device *hwdev,
u8 block)
{
switch (block) {
case MALIDP_SE_BLOCK:
return hwdev->map.se_base;
case MALIDP_DC_BLOCK:
return hwdev->map.dc_base;
}
return 0;
}
static inline void malidp_hw_disable_irq(struct malidp_hw_device *hwdev,
u8 block, u32 irq)
{
u32 base = malidp_get_block_base(hwdev, block);
malidp_hw_clearbits(hwdev, irq, base + MALIDP_REG_MASKIRQ);
}
static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev,
u8 block, u32 irq)
{
u32 base = malidp_get_block_base(hwdev, block);
malidp_hw_setbits(hwdev, irq, base + MALIDP_REG_MASKIRQ);
}
int malidp_de_irq_init(struct drm_device *drm, int irq);
void malidp_de_irq_fini(struct drm_device *drm);
int malidp_se_irq_init(struct drm_device *drm, int irq);
void malidp_se_irq_fini(struct drm_device *drm);
u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
u8 layer_id, u32 format);
/*
* 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
#endif /* __MALIDP_HW_H__ */
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Liviu Dudau <Liviu.Dudau@arm.com>
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* ARM Mali DP plane manipulation routines.
*/
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include "malidp_hw.h"
#include "malidp_drv.h"
/* Layer specific register offsets */
#define MALIDP_LAYER_FORMAT 0x000
#define MALIDP_LAYER_CONTROL 0x004
#define LAYER_ENABLE (1 << 0)
#define LAYER_ROT_OFFSET 8
#define LAYER_H_FLIP (1 << 10)
#define LAYER_V_FLIP (1 << 11)
#define LAYER_ROT_MASK (0xf << 8)
#define MALIDP_LAYER_SIZE 0x00c
#define LAYER_H_VAL(x) (((x) & 0x1fff) << 0)
#define LAYER_V_VAL(x) (((x) & 0x1fff) << 16)
#define MALIDP_LAYER_COMP_SIZE 0x010
#define MALIDP_LAYER_OFFSET 0x014
#define MALIDP_LAYER_STRIDE 0x018
static void malidp_de_plane_destroy(struct drm_plane *plane)
{
struct malidp_plane *mp = to_malidp_plane(plane);
if (mp->base.fb)
drm_framebuffer_unreference(mp->base.fb);
drm_plane_helper_disable(plane);
drm_plane_cleanup(plane);
devm_kfree(plane->dev->dev, mp);
}
struct drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane)
{
struct malidp_plane_state *state, *m_state;
if (!plane->state)
return NULL;
state = kmalloc(sizeof(*state), GFP_KERNEL);
if (state) {
m_state = to_malidp_plane_state(plane->state);
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
state->rotmem_size = m_state->rotmem_size;
}
return &state->base;
}
void malidp_destroy_plane_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct malidp_plane_state *m_state = to_malidp_plane_state(state);
__drm_atomic_helper_plane_destroy_state(state);
kfree(m_state);
}
static const struct drm_plane_funcs malidp_de_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = malidp_de_plane_destroy,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = malidp_duplicate_plane_state,
.atomic_destroy_state = malidp_destroy_plane_state,
};
static int malidp_de_plane_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct malidp_plane *mp = to_malidp_plane(plane);
struct malidp_plane_state *ms = to_malidp_plane_state(state);
u8 format_id;
u32 src_w, src_h;
if (!state->crtc || !state->fb)
return 0;
format_id = malidp_hw_get_format_id(&mp->hwdev->map, mp->layer->id,
state->fb->pixel_format);
if (format_id == MALIDP_INVALID_FORMAT_ID)
return -EINVAL;
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
if ((state->crtc_w > mp->hwdev->max_line_size) ||
(state->crtc_h > mp->hwdev->max_line_size) ||
(state->crtc_w < mp->hwdev->min_line_size) ||
(state->crtc_h < mp->hwdev->min_line_size) ||
(state->crtc_w != src_w) || (state->crtc_h != src_h))
return -EINVAL;
/* packed RGB888 / BGR888 can't be rotated or flipped */
if (state->rotation != BIT(DRM_ROTATE_0) &&
(state->fb->pixel_format == DRM_FORMAT_RGB888 ||
state->fb->pixel_format == DRM_FORMAT_BGR888))
return -EINVAL;
ms->rotmem_size = 0;
if (state->rotation & MALIDP_ROTATED_MASK) {
int val;
val = mp->hwdev->rotmem_required(mp->hwdev, state->crtc_h,
state->crtc_w,
state->fb->pixel_format);
if (val < 0)
return val;
ms->rotmem_size = val;
}
return 0;
}
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);
map = &mp->hwdev->map;
format = plane->state->fb->pixel_format;
format_id = malidp_hw_get_format_id(map, mp->layer->id, format);
num_planes = drm_format_num_planes(format);
/* convert src values from Q16 fixed point to integer */
src_w = plane->state->src_w >> 16;
src_h = plane->state->src_h >> 16;
if (plane->state->rotation & MALIDP_ROTATED_MASK) {
dest_w = plane->state->crtc_h;
dest_h = plane->state->crtc_w;
} else {
dest_w = plane->state->crtc_w;
dest_h = plane->state->crtc_h;
}
malidp_hw_write(mp->hwdev, format_id, mp->layer->base);
for (i = 0; i < num_planes; i++) {
/* calculate the offset for the layer's plane registers */
ptr = mp->layer->ptr + (i << 4);
obj = drm_fb_cma_get_gem_obj(plane->state->fb, i);
malidp_hw_write(mp->hwdev, lower_32_bits(obj->paddr), ptr);
malidp_hw_write(mp->hwdev, upper_32_bits(obj->paddr), ptr + 4);
malidp_hw_write(mp->hwdev, plane->state->fb->pitches[i],
mp->layer->base + MALIDP_LAYER_STRIDE);
}
malidp_hw_write(mp->hwdev, LAYER_H_VAL(src_w) | LAYER_V_VAL(src_h),
mp->layer->base + MALIDP_LAYER_SIZE);
malidp_hw_write(mp->hwdev, LAYER_H_VAL(dest_w) | LAYER_V_VAL(dest_h),
mp->layer->base + MALIDP_LAYER_COMP_SIZE);
malidp_hw_write(mp->hwdev, LAYER_H_VAL(plane->state->crtc_x) |
LAYER_V_VAL(plane->state->crtc_y),
mp->layer->base + MALIDP_LAYER_OFFSET);
/* first clear the rotation bits in the register */
malidp_hw_clearbits(mp->hwdev, LAYER_ROT_MASK,
mp->layer->base + MALIDP_LAYER_CONTROL);
/* setup the rotation and axis flip bits */
if (plane->state->rotation & DRM_ROTATE_MASK)
val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET;
if (plane->state->rotation & BIT(DRM_REFLECT_X))
val |= LAYER_V_FLIP;
if (plane->state->rotation & BIT(DRM_REFLECT_Y))
val |= LAYER_H_FLIP;
/* set the 'enable layer' bit */
val |= LAYER_ENABLE;
malidp_hw_setbits(mp->hwdev, val,
mp->layer->base + MALIDP_LAYER_CONTROL);
}
static void malidp_de_plane_disable(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct malidp_plane *mp = to_malidp_plane(plane);
malidp_hw_clearbits(mp->hwdev, LAYER_ENABLE,
mp->layer->base + MALIDP_LAYER_CONTROL);
}
static const struct drm_plane_helper_funcs malidp_de_plane_helper_funcs = {
.atomic_check = malidp_de_plane_check,
.atomic_update = malidp_de_plane_update,
.atomic_disable = malidp_de_plane_disable,
};
int malidp_de_planes_init(struct drm_device *drm)
{
struct malidp_drm *malidp = drm->dev_private;
const struct malidp_hw_regmap *map = &malidp->dev->map;
struct malidp_plane *plane = NULL;
enum drm_plane_type plane_type;
unsigned long crtcs = 1 << drm->mode_config.num_crtc;
u32 *formats;
int ret, i, j, n;
formats = kcalloc(map->n_input_formats, sizeof(*formats), GFP_KERNEL);
if (!formats) {
ret = -ENOMEM;
goto cleanup;
}
for (i = 0; i < map->n_layers; i++) {
u8 id = map->layers[i].id;
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
if (!plane) {
ret = -ENOMEM;
goto cleanup;
}
/* build the list of DRM supported formats based on the map */
for (n = 0, j = 0; j < map->n_input_formats; j++) {
if ((map->input_formats[j].layer & id) == id)
formats[n++] = map->input_formats[j].format;
}
plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY;
ret = drm_universal_plane_init(drm, &plane->base, crtcs,
&malidp_de_plane_funcs, formats,
n, plane_type, NULL);
if (ret < 0)
goto cleanup;
if (!drm->mode_config.rotation_property) {
unsigned long flags = BIT(DRM_ROTATE_0) |
BIT(DRM_ROTATE_90) |
BIT(DRM_ROTATE_180) |
BIT(DRM_ROTATE_270) |
BIT(DRM_REFLECT_X) |
BIT(DRM_REFLECT_Y);
drm->mode_config.rotation_property =
drm_mode_create_rotation_property(drm, flags);
}
/* SMART layer can't be rotated */
if (drm->mode_config.rotation_property && (id != DE_SMART))
drm_object_attach_property(&plane->base.base,
drm->mode_config.rotation_property,
BIT(DRM_ROTATE_0));
drm_plane_helper_add(&plane->base,
&malidp_de_plane_helper_funcs);
plane->hwdev = malidp->dev;
plane->layer = &map->layers[i];
}
kfree(formats);
return 0;
cleanup:
malidp_de_planes_destroy(drm);
kfree(formats);
return ret;
}
void malidp_de_planes_destroy(struct drm_device *drm)
{
struct drm_plane *p, *pt;
list_for_each_entry_safe(p, pt, &drm->mode_config.plane_list, head) {
drm_plane_cleanup(p);
kfree(p);
}
}
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Liviu Dudau <Liviu.Dudau@arm.com>
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* ARM Mali DP500/DP550/DP650 registers definition.
*/
#ifndef __MALIDP_REGS_H__
#define __MALIDP_REGS_H__
/*
* abbreviations used:
* - DC - display core (general settings)
* - DE - display engine
* - SE - scaling engine
*/
/* interrupt bit masks */
#define MALIDP_DE_IRQ_UNDERRUN (1 << 0)
#define MALIDP500_DE_IRQ_AXI_ERR (1 << 4)
#define MALIDP500_DE_IRQ_VSYNC (1 << 5)
#define MALIDP500_DE_IRQ_PROG_LINE (1 << 6)
#define MALIDP500_DE_IRQ_SATURATION (1 << 7)
#define MALIDP500_DE_IRQ_CONF_VALID (1 << 8)
#define MALIDP500_DE_IRQ_CONF_MODE (1 << 11)
#define MALIDP500_DE_IRQ_CONF_ACTIVE (1 << 17)
#define MALIDP500_DE_IRQ_PM_ACTIVE (1 << 18)
#define MALIDP500_DE_IRQ_TESTMODE_ACTIVE (1 << 19)
#define MALIDP500_DE_IRQ_FORCE_BLNK_ACTIVE (1 << 24)
#define MALIDP500_DE_IRQ_AXI_BUSY (1 << 28)
#define MALIDP500_DE_IRQ_GLOBAL (1 << 31)
#define MALIDP500_SE_IRQ_CONF_MODE (1 << 0)
#define MALIDP500_SE_IRQ_CONF_VALID (1 << 4)
#define MALIDP500_SE_IRQ_INIT_BUSY (1 << 5)
#define MALIDP500_SE_IRQ_AXI_ERROR (1 << 8)
#define MALIDP500_SE_IRQ_OVERRUN (1 << 9)
#define MALIDP500_SE_IRQ_PROG_LINE1 (1 << 12)
#define MALIDP500_SE_IRQ_PROG_LINE2 (1 << 13)
#define MALIDP500_SE_IRQ_CONF_ACTIVE (1 << 17)
#define MALIDP500_SE_IRQ_PM_ACTIVE (1 << 18)
#define MALIDP500_SE_IRQ_AXI_BUSY (1 << 28)
#define MALIDP500_SE_IRQ_GLOBAL (1 << 31)
#define MALIDP550_DE_IRQ_SATURATION (1 << 8)
#define MALIDP550_DE_IRQ_VSYNC (1 << 12)
#define MALIDP550_DE_IRQ_PROG_LINE (1 << 13)
#define MALIDP550_DE_IRQ_AXI_ERR (1 << 16)
#define MALIDP550_SE_IRQ_EOW (1 << 0)
#define MALIDP550_SE_IRQ_AXI_ERR (1 << 16)
#define MALIDP550_DC_IRQ_CONF_VALID (1 << 0)
#define MALIDP550_DC_IRQ_CONF_MODE (1 << 4)
#define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16)
#define MALIDP550_DC_IRQ_DE (1 << 20)
#define MALIDP550_DC_IRQ_SE (1 << 24)
#define MALIDP650_DE_IRQ_DRIFT (1 << 4)
/* bit masks that are common between products */
#define MALIDP_CFG_VALID (1 << 0)
#define MALIDP_DISP_FUNC_ILACED (1 << 8)
/* register offsets for IRQ management */
#define MALIDP_REG_STATUS 0x00000
#define MALIDP_REG_SETIRQ 0x00004
#define MALIDP_REG_MASKIRQ 0x00008
#define MALIDP_REG_CLEARIRQ 0x0000c
/* register offsets */
#define MALIDP_DE_CORE_ID 0x00018
#define MALIDP_DE_DISPLAY_FUNC 0x00020
/* these offsets are relative to MALIDP5x0_TIMINGS_BASE */
#define MALIDP_DE_H_TIMINGS 0x0
#define MALIDP_DE_V_TIMINGS 0x4
#define MALIDP_DE_SYNC_WIDTH 0x8
#define MALIDP_DE_HV_ACTIVE 0xc
/* macros to set values into registers */
#define MALIDP_DE_H_FRONTPORCH(x) (((x) & 0xfff) << 0)
#define MALIDP_DE_H_BACKPORCH(x) (((x) & 0x3ff) << 16)
#define MALIDP500_DE_V_FRONTPORCH(x) (((x) & 0xff) << 0)
#define MALIDP550_DE_V_FRONTPORCH(x) (((x) & 0xfff) << 0)
#define MALIDP_DE_V_BACKPORCH(x) (((x) & 0xff) << 16)
#define MALIDP_DE_H_SYNCWIDTH(x) (((x) & 0x3ff) << 0)
#define MALIDP_DE_V_SYNCWIDTH(x) (((x) & 0xff) << 16)
#define MALIDP_DE_H_ACTIVE(x) (((x) & 0x1fff) << 0)
#define MALIDP_DE_V_ACTIVE(x) (((x) & 0x1fff) << 16)
/* register offsets and bits specific to DP500 */
#define MALIDP500_DC_BASE 0x00000
#define MALIDP500_DC_CONTROL 0x0000c
#define MALIDP500_DC_CONFIG_REQ (1 << 17)
#define MALIDP500_HSYNCPOL (1 << 20)
#define MALIDP500_VSYNCPOL (1 << 21)
#define MALIDP500_DC_CLEAR_MASK 0x300fff
#define MALIDP500_DE_LINE_COUNTER 0x00010
#define MALIDP500_DE_AXI_CONTROL 0x00014
#define MALIDP500_DE_SECURE_CTRL 0x0001c
#define MALIDP500_DE_CHROMA_KEY 0x00024
#define MALIDP500_TIMINGS_BASE 0x00028
#define MALIDP500_CONFIG_3D 0x00038
#define MALIDP500_BGND_COLOR 0x0003c
#define MALIDP500_OUTPUT_DEPTH 0x00044
#define MALIDP500_YUV_RGB_COEF 0x00048
#define MALIDP500_COLOR_ADJ_COEF 0x00078
#define MALIDP500_COEF_TABLE_ADDR 0x000a8
#define MALIDP500_COEF_TABLE_DATA 0x000ac
#define MALIDP500_DE_LV_BASE 0x00100
#define MALIDP500_DE_LV_PTR_BASE 0x00124
#define MALIDP500_DE_LG1_BASE 0x00200
#define MALIDP500_DE_LG1_PTR_BASE 0x0021c
#define MALIDP500_DE_LG2_BASE 0x00300
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
#define MALIDP500_SE_BASE 0x00c00
#define MALIDP500_SE_PTR_BASE 0x00e0c
#define MALIDP500_DC_IRQ_BASE 0x00f00
#define MALIDP500_CONFIG_VALID 0x00f00
#define MALIDP500_CONFIG_ID 0x00fd4
/* register offsets and bits specific to DP550/DP650 */
#define MALIDP550_DE_CONTROL 0x00010
#define MALIDP550_DE_LINE_COUNTER 0x00014
#define MALIDP550_DE_AXI_CONTROL 0x00018
#define MALIDP550_DE_QOS 0x0001c
#define MALIDP550_TIMINGS_BASE 0x00030
#define MALIDP550_HSYNCPOL (1 << 12)
#define MALIDP550_VSYNCPOL (1 << 28)
#define MALIDP550_DE_DISP_SIDEBAND 0x00040
#define MALIDP550_DE_BGND_COLOR 0x00044
#define MALIDP550_DE_OUTPUT_DEPTH 0x0004c
#define MALIDP550_DE_COLOR_COEF 0x00050
#define MALIDP550_DE_COEF_TABLE_ADDR 0x00080
#define MALIDP550_DE_COEF_TABLE_DATA 0x00084
#define MALIDP550_DE_LV1_BASE 0x00100
#define MALIDP550_DE_LV1_PTR_BASE 0x00124
#define MALIDP550_DE_LV2_BASE 0x00200
#define MALIDP550_DE_LV2_PTR_BASE 0x00224
#define MALIDP550_DE_LG_BASE 0x00300
#define MALIDP550_DE_LG_PTR_BASE 0x0031c
#define MALIDP550_DE_LS_BASE 0x00400
#define MALIDP550_DE_LS_PTR_BASE 0x0042c
#define MALIDP550_DE_PERF_BASE 0x00500
#define MALIDP550_SE_BASE 0x08000
#define MALIDP550_DC_BASE 0x0c000
#define MALIDP550_DC_CONTROL 0x0c010
#define MALIDP550_DC_CONFIG_REQ (1 << 16)
#define MALIDP550_CONFIG_VALID 0x0c014
#define MALIDP550_CONFIG_ID 0x0ffd4
/*
* Starting with DP550 the register map blocks has been standardised to the
* following layout:
*
* Offset Block registers
* 0x00000 Display Engine
* 0x08000 Scaling Engine
* 0x0c000 Display Core
* 0x10000 Secure control
*
* The old DP500 IP mixes some DC with the DE registers, hence the need
* for a mapping structure.
*/
#endif /* __MALIDP_REGS_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment