Commit 5ebffda2 authored by Dave Airlie's avatar Dave Airlie

Merge commit 'refs/for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next

This pull requests adds initial Mali D71 support into the Arm "komeda" DRM
driver. The code has been reviewed at the end of last year, I just been
too slow with pushing it into mainline. Since it started baking in
linux-next we had a kbuild-bot issue raised and one from Joe Perches on
the MAINTAINERS entry, for which I'm including fixes here.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Liviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190401192833.GW21747@e110455-lin.cambridge.arm.com
parents 45710982 04c8a1ec
......@@ -1167,7 +1167,7 @@ S: Supported
T: git git://linux-arm.org/linux-ld.git for-upstream/mali-dp
F: drivers/gpu/drm/arm/display/include/
F: drivers/gpu/drm/arm/display/komeda/
F: Documentation/devicetree/bindings/display/arm/arm,komeda.txt
F: Documentation/devicetree/bindings/display/arm,komeda.txt
F: Documentation/gpu/komeda-kms.rst
ARM MALI-DP DRM DRIVER
......
......@@ -7,10 +7,41 @@
#ifndef _MALIDP_UTILS_
#define _MALIDP_UTILS_
#include <linux/delay.h>
#define has_bit(nr, mask) (BIT(nr) & (mask))
#define has_bits(bits, mask) (((bits) & (mask)) == (bits))
#define dp_for_each_set_bit(bit, mask) \
for_each_set_bit((bit), ((unsigned long *)&(mask)), sizeof(mask) * 8)
#define dp_wait_cond(__cond, __tries, __min_range, __max_range) \
({ \
int num_tries = __tries; \
while (!__cond && (num_tries > 0)) { \
usleep_range(__min_range, __max_range); \
if (__cond) \
break; \
num_tries--; \
} \
num_tries; \
})
/* the restriction of range is [start, end] */
struct malidp_range {
u32 start;
u32 end;
};
static inline void set_range(struct malidp_range *rg, u32 start, u32 end)
{
rg->start = start;
rg->end = end;
}
static inline bool in_range(struct malidp_range *rg, u32 v)
{
return (v >= rg->start) && (v <= rg->end);
}
#endif /* _MALIDP_UTILS_ */
......@@ -16,6 +16,7 @@ komeda-y := \
komeda_private_obj.o
komeda-y += \
d71/d71_dev.o
d71/d71_dev.o \
d71/d71_component.o
obj-$(CONFIG_DRM_KOMEDA) += komeda.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
* Author: James.Qian.Wang <james.qian.wang@arm.com>
*
*/
#ifndef _D71_DEV_H_
#define _D71_DEV_H_
#include "komeda_dev.h"
#include "komeda_pipeline.h"
#include "d71_regs.h"
struct d71_pipeline {
struct komeda_pipeline base;
/* d71 private pipeline blocks */
u32 __iomem *lpu_addr;
u32 __iomem *cu_addr;
u32 __iomem *dou_addr;
u32 __iomem *dou_ft_coeff_addr; /* forward transform coeffs table */
};
struct d71_dev {
struct komeda_dev *mdev;
int num_blocks;
int num_pipelines;
int num_rich_layers;
u32 max_line_size;
u32 max_vsize;
u32 supports_dual_link : 1;
u32 integrates_tbu : 1;
/* global register blocks */
u32 __iomem *gcu_addr;
/* scaling coeffs table */
u32 __iomem *glb_scl_coeff_addr[D71_MAX_GLB_SCL_COEFF];
u32 __iomem *periph_addr;
struct d71_pipeline *pipes[D71_MAX_PIPELINE];
};
#define to_d71_pipeline(x) container_of(x, struct d71_pipeline, base)
int d71_probe_block(struct d71_dev *d71,
struct block_header *blk, u32 __iomem *reg);
void d71_read_block_header(u32 __iomem *reg, struct block_header *blk);
#endif /* !_D71_DEV_H_ */
This diff is collapsed.
......@@ -18,6 +18,24 @@
#include "komeda_dev.h"
#include "komeda_kms.h"
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
struct komeda_events *evts)
{
struct drm_crtc *crtc = &kcrtc->base;
u32 events = evts->pipes[kcrtc->master->id];
if (events & KOMEDA_EVENT_VSYNC)
drm_crtc_handle_vblank(crtc);
/* will handle it together with the write back support */
if (events & KOMEDA_EVENT_EOW)
DRM_DEBUG("EOW.\n");
/* will handle it with crtc->flush */
if (events & KOMEDA_EVENT_FLIP)
DRM_DEBUG("FLIP Done.\n");
}
struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
};
......
......@@ -8,11 +8,57 @@
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#endif
#include <drm/drm_print.h>
#include "komeda_dev.h"
static int komeda_register_show(struct seq_file *sf, void *x)
{
struct komeda_dev *mdev = sf->private;
int i;
if (mdev->funcs->dump_register)
mdev->funcs->dump_register(mdev, sf);
for (i = 0; i < mdev->n_pipelines; i++)
komeda_pipeline_dump_register(mdev->pipelines[i], sf);
return 0;
}
static int komeda_register_open(struct inode *inode, struct file *filp)
{
return single_open(filp, komeda_register_show, inode->i_private);
}
static const struct file_operations komeda_register_fops = {
.owner = THIS_MODULE,
.open = komeda_register_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#ifdef CONFIG_DEBUG_FS
static void komeda_debugfs_init(struct komeda_dev *mdev)
{
if (!debugfs_initialized())
return;
mdev->debugfs_root = debugfs_create_dir("komeda", NULL);
if (IS_ERR_OR_NULL(mdev->debugfs_root))
return;
debugfs_create_file("register", 0444, mdev->debugfs_root,
mdev, &komeda_register_fops);
}
#endif
static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
{
struct komeda_pipeline *pipe;
......@@ -53,6 +99,7 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
{
struct platform_device *pdev = to_platform_device(dev);
struct device_node *child, *np = dev->of_node;
struct clk *clk;
int ret;
......@@ -62,6 +109,11 @@ static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
return PTR_ERR(clk);
mdev->mclk = clk;
mdev->irq = platform_get_irq(pdev, 0);
if (mdev->irq < 0) {
DRM_ERROR("could not get IRQ number.\n");
return mdev->irq;
}
for_each_available_child_of_node(np, child) {
if (of_node_cmp(child->name, "pipeline") == 0) {
......@@ -147,6 +199,16 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
goto err_cleanup;
}
err = komeda_assemble_pipelines(mdev);
if (err) {
DRM_ERROR("assemble display pipelines failed.\n");
goto err_cleanup;
}
#ifdef CONFIG_DEBUG_FS
komeda_debugfs_init(mdev);
#endif
return mdev;
err_cleanup:
......@@ -160,6 +222,10 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
struct komeda_dev_funcs *funcs = mdev->funcs;
int i;
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(mdev->debugfs_root);
#endif
for (i = 0; i < mdev->n_pipelines; i++) {
komeda_pipeline_destroy(mdev, mdev->pipelines[i]);
mdev->pipelines[i] = NULL;
......
......@@ -13,6 +13,33 @@
#include "malidp_product.h"
#include "komeda_format_caps.h"
#define KOMEDA_EVENT_VSYNC BIT_ULL(0)
#define KOMEDA_EVENT_FLIP BIT_ULL(1)
#define KOMEDA_EVENT_URUN BIT_ULL(2)
#define KOMEDA_EVENT_IBSY BIT_ULL(3)
#define KOMEDA_EVENT_OVR BIT_ULL(4)
#define KOMEDA_EVENT_EOW BIT_ULL(5)
#define KOMEDA_EVENT_MODE BIT_ULL(6)
#define KOMEDA_ERR_TETO BIT_ULL(14)
#define KOMEDA_ERR_TEMR BIT_ULL(15)
#define KOMEDA_ERR_TITR BIT_ULL(16)
#define KOMEDA_ERR_CPE BIT_ULL(17)
#define KOMEDA_ERR_CFGE BIT_ULL(18)
#define KOMEDA_ERR_AXIE BIT_ULL(19)
#define KOMEDA_ERR_ACE0 BIT_ULL(20)
#define KOMEDA_ERR_ACE1 BIT_ULL(21)
#define KOMEDA_ERR_ACE2 BIT_ULL(22)
#define KOMEDA_ERR_ACE3 BIT_ULL(23)
#define KOMEDA_ERR_DRIFTTO BIT_ULL(24)
#define KOMEDA_ERR_FRAMETO BIT_ULL(25)
#define KOMEDA_ERR_CSCE BIT_ULL(26)
#define KOMEDA_ERR_ZME BIT_ULL(27)
#define KOMEDA_ERR_MERR BIT_ULL(28)
#define KOMEDA_ERR_TCF BIT_ULL(29)
#define KOMEDA_ERR_TTNG BIT_ULL(30)
#define KOMEDA_ERR_TTF BIT_ULL(31)
/* malidp device id */
enum {
MALI_D71 = 0,
......@@ -39,6 +66,11 @@ struct komeda_product_data {
struct komeda_dev;
struct komeda_events {
u64 global;
u64 pipes[KOMEDA_MAX_PIPELINES];
};
/**
* struct komeda_dev_funcs
*
......@@ -60,6 +92,20 @@ struct komeda_dev_funcs {
int (*enum_resources)(struct komeda_dev *mdev);
/** @cleanup: call to chip to cleanup komeda_dev->chip data */
void (*cleanup)(struct komeda_dev *mdev);
/**
* @irq_handler:
*
* for CORE to get the HW event from the CHIP when interrupt happened.
*/
irqreturn_t (*irq_handler)(struct komeda_dev *mdev,
struct komeda_events *events);
/** @enable_irq: enable irq */
int (*enable_irq)(struct komeda_dev *mdev);
/** @disable_irq: disable irq */
int (*disable_irq)(struct komeda_dev *mdev);
/** @dump_register: Optional, dump registers to seq_file */
void (*dump_register)(struct komeda_dev *mdev, struct seq_file *seq);
};
/**
......@@ -81,6 +127,9 @@ struct komeda_dev {
/** @mck: HW main engine clk */
struct clk *mclk;
/** @irq: irq number */
int irq;
int n_pipelines;
struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];
......@@ -93,6 +142,8 @@ struct komeda_dev {
* destroyed by &komeda_dev_funcs.cleanup()
*/
void *chip_data;
struct dentry *debugfs_root;
};
static inline bool
......
......@@ -13,6 +13,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_irq.h>
#include <drm/drm_vblank.h>
#include "komeda_dev.h"
......@@ -33,10 +34,31 @@ static int komeda_gem_cma_dumb_create(struct drm_file *file,
return drm_gem_cma_dumb_create_internal(file, dev, args);
}
static irqreturn_t komeda_kms_irq_handler(int irq, void *data)
{
struct drm_device *drm = data;
struct komeda_dev *mdev = drm->dev_private;
struct komeda_kms_dev *kms = to_kdev(drm);
struct komeda_events evts;
irqreturn_t status;
u32 i;
/* Call into the CHIP to recognize events */
memset(&evts, 0, sizeof(evts));
status = mdev->funcs->irq_handler(mdev, &evts);
/* Notify the crtc to handle the events */
for (i = 0; i < kms->n_crtcs; i++)
komeda_crtc_handle_event(&kms->crtcs[i], &evts);
return status;
}
static struct drm_driver komeda_kms_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
DRIVER_PRIME,
DRIVER_PRIME | DRIVER_HAVE_IRQ,
.lastclose = drm_fb_helper_lastclose,
.irq_handler = komeda_kms_irq_handler,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = komeda_gem_cma_dumb_create,
......@@ -144,12 +166,22 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
drm_mode_config_reset(drm);
err = drm_dev_register(drm, 0);
err = drm_irq_install(drm, mdev->irq);
if (err)
goto cleanup_mode_config;
err = mdev->funcs->enable_irq(mdev);
if (err)
goto uninstall_irq;
err = drm_dev_register(drm, 0);
if (err)
goto uninstall_irq;
return kms;
uninstall_irq:
drm_irq_uninstall(drm);
cleanup_mode_config:
drm_mode_config_cleanup(drm);
free_kms:
......@@ -162,7 +194,9 @@ void komeda_kms_detach(struct komeda_kms_dev *kms)
struct drm_device *drm = &kms->base;
struct komeda_dev *mdev = drm->dev_private;
mdev->funcs->disable_irq(mdev);
drm_dev_unregister(drm);
drm_irq_uninstall(drm);
component_unbind_all(mdev->dev, drm);
komeda_kms_cleanup_private_objs(mdev);
drm_mode_config_cleanup(drm);
......
......@@ -12,6 +12,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_writeback.h>
#include <video/videomode.h>
#include <video/display_timing.h>
/** struct komeda_plane - komeda instance of drm_plane */
struct komeda_plane {
......@@ -108,6 +110,9 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
struct komeda_dev *mdev);
void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev);
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
struct komeda_events *evts);
struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev);
void komeda_kms_detach(struct komeda_kms_dev *kms);
......
......@@ -19,17 +19,17 @@ komeda_pipeline_add(struct komeda_dev *mdev, size_t size,
if (mdev->n_pipelines + 1 > KOMEDA_MAX_PIPELINES) {
DRM_ERROR("Exceed max support %d pipelines.\n",
KOMEDA_MAX_PIPELINES);
return NULL;
return ERR_PTR(-ENOSPC);
}
if (size < sizeof(*pipe)) {
DRM_ERROR("Request pipeline size too small.\n");
return NULL;
return ERR_PTR(-EINVAL);
}
pipe = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
if (!pipe)
return NULL;
return ERR_PTR(-ENOMEM);
pipe->mdev = mdev;
pipe->id = mdev->n_pipelines;
......@@ -142,32 +142,32 @@ komeda_component_add(struct komeda_pipeline *pipe,
if (max_active_inputs > KOMEDA_COMPONENT_N_INPUTS) {
WARN(1, "please large KOMEDA_COMPONENT_N_INPUTS to %d.\n",
max_active_inputs);
return NULL;
return ERR_PTR(-ENOSPC);
}
pos = komeda_pipeline_get_component_pos(pipe, id);
if (!pos || (*pos))
return NULL;
return ERR_PTR(-EINVAL);
if (has_bit(id, KOMEDA_PIPELINE_LAYERS)) {
idx = id - KOMEDA_COMPONENT_LAYER0;
num = &pipe->n_layers;
if (idx != pipe->n_layers) {
DRM_ERROR("please add Layer by id sequence.\n");
return NULL;
return ERR_PTR(-EINVAL);
}
} else if (has_bit(id, KOMEDA_PIPELINE_SCALERS)) {
idx = id - KOMEDA_COMPONENT_SCALER0;
num = &pipe->n_scalers;
if (idx != pipe->n_scalers) {
DRM_ERROR("please add Scaler by id sequence.\n");
return NULL;
return ERR_PTR(-EINVAL);
}
}
c = devm_kzalloc(pipe->mdev->dev, comp_sz, GFP_KERNEL);
if (!c)
return NULL;
return ERR_PTR(-ENOMEM);
c->id = id;
c->hw_id = hw_id;
......@@ -200,3 +200,98 @@ void komeda_component_destroy(struct komeda_dev *mdev,
{
devm_kfree(mdev->dev, c);
}
static void komeda_component_dump(struct komeda_component *c)
{
if (!c)
return;
DRM_DEBUG(" %s: ID %d-0x%08lx.\n",
c->name, c->id, BIT(c->id));
DRM_DEBUG(" max_active_inputs:%d, supported_inputs: 0x%08x.\n",
c->max_active_inputs, c->supported_inputs);
DRM_DEBUG(" max_active_outputs:%d, supported_outputs: 0x%08x.\n",
c->max_active_outputs, c->supported_outputs);
}
static void komeda_pipeline_dump(struct komeda_pipeline *pipe)
{
struct komeda_component *c;
int id;
DRM_INFO("Pipeline-%d: n_layers: %d, n_scalers: %d, output: %s\n",
pipe->id, pipe->n_layers, pipe->n_scalers,
pipe->of_output_dev ? pipe->of_output_dev->full_name : "none");
dp_for_each_set_bit(id, pipe->avail_comps) {
c = komeda_pipeline_get_component(pipe, id);
komeda_component_dump(c);
}
}
static void komeda_component_verify_inputs(struct komeda_component *c)
{
struct komeda_pipeline *pipe = c->pipeline;
struct komeda_component *input;
int id;
dp_for_each_set_bit(id, c->supported_inputs) {
input = komeda_pipeline_get_component(pipe, id);
if (!input) {
c->supported_inputs &= ~(BIT(id));
DRM_WARN("Can not find input(ID-%d) for component: %s.\n",
id, c->name);
continue;
}
input->supported_outputs |= BIT(c->id);
}
}
static void komeda_pipeline_assemble(struct komeda_pipeline *pipe)
{
struct komeda_component *c;
int id;
dp_for_each_set_bit(id, pipe->avail_comps) {
c = komeda_pipeline_get_component(pipe, id);
komeda_component_verify_inputs(c);
}
}
int komeda_assemble_pipelines(struct komeda_dev *mdev)
{
struct komeda_pipeline *pipe;
int i;
for (i = 0; i < mdev->n_pipelines; i++) {
pipe = mdev->pipelines[i];
komeda_pipeline_assemble(pipe);
komeda_pipeline_dump(pipe);
}
return 0;
}
void komeda_pipeline_dump_register(struct komeda_pipeline *pipe,
struct seq_file *sf)
{
struct komeda_component *c;
u32 id;
seq_printf(sf, "\n======== Pipeline-%d ==========\n", pipe->id);
if (pipe->funcs && pipe->funcs->dump_register)
pipe->funcs->dump_register(pipe, sf);
dp_for_each_set_bit(id, pipe->avail_comps) {
c = komeda_pipeline_get_component(pipe, id);
seq_printf(sf, "\n------%s------\n", c->name);
if (c->funcs->dump_register)
c->funcs->dump_register(c, sf);
}
}
......@@ -204,51 +204,74 @@ static inline u16 component_changed_inputs(struct komeda_component_state *st)
return component_disabling_inputs(st) | st->changed_active_inputs;
}
#define for_each_changed_input(st, i) \
for ((i) = 0; (i) < (st)->component->max_active_inputs; (i)++) \
if (has_bit((i), component_changed_inputs(st)))
#define to_comp(__c) (((__c) == NULL) ? NULL : &((__c)->base))
#define to_cpos(__c) ((struct komeda_component **)&(__c))
/* these structures are going to be filled in in uture patches */
struct komeda_layer {
struct komeda_component base;
/* layer specific features and caps */
int layer_type; /* RICH, SIMPLE or WB */
/* accepted h/v input range before rotation */
struct malidp_range hsize_in, vsize_in;
u32 layer_type; /* RICH, SIMPLE or WB */
u32 supported_rots;
};
struct komeda_layer_state {
struct komeda_component_state base;
/* layer specific configuration state */
u16 hsize, vsize;
u32 rot;
dma_addr_t addr[3];
};
struct komeda_compiz {
struct komeda_scaler {
struct komeda_component base;
/* compiz specific features and caps */
/* scaler features and caps */
};
struct komeda_compiz_state {
struct komeda_scaler_state {
struct komeda_component_state base;
/* compiz specific configuration state */
};
struct komeda_scaler {
struct komeda_compiz {
struct komeda_component base;
/* scaler features and caps */
struct malidp_range hsize, vsize;
};
struct komeda_scaler_state {
struct komeda_compiz_input_cfg {
u16 hsize, vsize;
u16 hoffset, voffset;
u8 pixel_blend_mode, layer_alpha;
};
struct komeda_compiz_state {
struct komeda_component_state base;
/* composition size */
u16 hsize, vsize;
struct komeda_compiz_input_cfg cins[KOMEDA_COMPONENT_N_INPUTS];
};
struct komeda_improc {
struct komeda_component base;
u32 supported_color_formats; /* DRM_RGB/YUV444/YUV420*/
u32 supported_color_depths; /* BIT(8) | BIT(10)*/
u8 supports_degamma : 1;
u8 supports_csc : 1;
u8 supports_gamma : 1;
};
struct komeda_improc_state {
struct komeda_component_state base;
u16 hsize, vsize;
};
/* display timing controller */
struct komeda_timing_ctrlr {
struct komeda_component base;
u8 supports_dual_link : 1;
};
struct komeda_timing_ctrlr_state {
......@@ -340,10 +363,13 @@ komeda_pipeline_add(struct komeda_dev *mdev, size_t size,
struct komeda_pipeline_funcs *funcs);
void komeda_pipeline_destroy(struct komeda_dev *mdev,
struct komeda_pipeline *pipe);
int komeda_assemble_pipelines(struct komeda_dev *mdev);
struct komeda_component *
komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id);
void komeda_pipeline_dump_register(struct komeda_pipeline *pipe,
struct seq_file *sf);
/* component APIs */
struct komeda_component *
komeda_component_add(struct komeda_pipeline *pipe,
......
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