Commit 4da1d4c7 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

"mali-dp driver changes for drm-next, includes the driver implementation
for writeback, improvements for power management handling in the driver
and a debugfs entry for reporting possible internal errors. Please pull
at your earliest convenience.

Boris Brezillon is also interested in this pull as he is going to change
slightly the parameter for the writeback connector's atomic_commit() and
he needs to fix the mali-dp driver in his series."
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180705144408.GH15340@e110455-lin.cambridge.arm.com
parents a1c3b495 e368fc75
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
obj-$(CONFIG_DRM_HDLCD) += hdlcd.o obj-$(CONFIG_DRM_HDLCD) += hdlcd.o
mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
mali-dp-y += malidp_mw.o
obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o
...@@ -411,6 +411,16 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc, ...@@ -411,6 +411,16 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
} }
} }
/* If only the writeback routing has changed, we don't need a modeset */
if (state->connectors_changed) {
u32 old_mask = crtc->state->connector_mask;
u32 new_mask = state->connector_mask;
if ((old_mask ^ new_mask) ==
(1 << drm_connector_index(&malidp->mw_connector.base)))
state->connectors_changed = false;
}
ret = malidp_crtc_atomic_check_gamma(crtc, state); ret = malidp_crtc_atomic_check_gamma(crtc, state);
ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state); ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state); ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/of_graph.h> #include <linux/of_graph.h>
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/debugfs.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
...@@ -31,6 +32,7 @@ ...@@ -31,6 +32,7 @@
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include "malidp_drv.h" #include "malidp_drv.h"
#include "malidp_mw.h"
#include "malidp_regs.h" #include "malidp_regs.h"
#include "malidp_hw.h" #include "malidp_hw.h"
...@@ -170,14 +172,15 @@ static int malidp_set_and_wait_config_valid(struct drm_device *drm) ...@@ -170,14 +172,15 @@ static int malidp_set_and_wait_config_valid(struct drm_device *drm)
struct malidp_hw_device *hwdev = malidp->dev; struct malidp_hw_device *hwdev = malidp->dev;
int ret; int ret;
atomic_set(&malidp->config_valid, 0); hwdev->hw->set_config_valid(hwdev, 1);
hwdev->hw->set_config_valid(hwdev);
/* don't wait for config_valid flag if we are in config mode */ /* don't wait for config_valid flag if we are in config mode */
if (hwdev->hw->in_config_mode(hwdev)) if (hwdev->hw->in_config_mode(hwdev)) {
atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE);
return 0; return 0;
}
ret = wait_event_interruptible_timeout(malidp->wq, ret = wait_event_interruptible_timeout(malidp->wq,
atomic_read(&malidp->config_valid) == 1, atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE,
msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));
return (ret > 0) ? 0 : -ETIMEDOUT; return (ret > 0) ? 0 : -ETIMEDOUT;
...@@ -216,12 +219,20 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) ...@@ -216,12 +219,20 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
static void malidp_atomic_commit_tail(struct drm_atomic_state *state) static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
{ {
struct drm_device *drm = state->dev; struct drm_device *drm = state->dev;
struct malidp_drm *malidp = drm->dev_private;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state; struct drm_crtc_state *old_crtc_state;
int i; int i;
pm_runtime_get_sync(drm->dev); pm_runtime_get_sync(drm->dev);
/*
* set config_valid to a special value to let IRQ handlers
* know that we are updating registers
*/
atomic_set(&malidp->config_valid, MALIDP_CONFIG_START);
malidp->dev->hw->set_config_valid(malidp->dev, 0);
drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_disables(drm, state);
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
...@@ -230,7 +241,9 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) ...@@ -230,7 +241,9 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
malidp_atomic_commit_se_config(crtc, old_crtc_state); malidp_atomic_commit_se_config(crtc, old_crtc_state);
} }
drm_atomic_helper_commit_planes(drm, state, 0); drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
malidp_mw_atomic_commit(drm, state);
drm_atomic_helper_commit_modeset_enables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state);
...@@ -268,12 +281,18 @@ static int malidp_init(struct drm_device *drm) ...@@ -268,12 +281,18 @@ static int malidp_init(struct drm_device *drm)
drm->mode_config.helper_private = &malidp_mode_config_helpers; drm->mode_config.helper_private = &malidp_mode_config_helpers;
ret = malidp_crtc_init(drm); ret = malidp_crtc_init(drm);
if (ret) { if (ret)
drm_mode_config_cleanup(drm); goto crtc_fail;
return ret;
} ret = malidp_mw_connector_init(drm);
if (ret)
goto crtc_fail;
return 0; return 0;
crtc_fail:
drm_mode_config_cleanup(drm);
return ret;
} }
static void malidp_fini(struct drm_device *drm) static void malidp_fini(struct drm_device *drm)
...@@ -285,6 +304,8 @@ static int malidp_irq_init(struct platform_device *pdev) ...@@ -285,6 +304,8 @@ static int malidp_irq_init(struct platform_device *pdev)
{ {
int irq_de, irq_se, ret = 0; int irq_de, irq_se, ret = 0;
struct drm_device *drm = dev_get_drvdata(&pdev->dev); struct drm_device *drm = dev_get_drvdata(&pdev->dev);
struct malidp_drm *malidp = drm->dev_private;
struct malidp_hw_device *hwdev = malidp->dev;
/* fetch the interrupts from DT */ /* fetch the interrupts from DT */
irq_de = platform_get_irq_byname(pdev, "DE"); irq_de = platform_get_irq_byname(pdev, "DE");
...@@ -304,7 +325,7 @@ static int malidp_irq_init(struct platform_device *pdev) ...@@ -304,7 +325,7 @@ static int malidp_irq_init(struct platform_device *pdev)
ret = malidp_se_irq_init(drm, irq_se); ret = malidp_se_irq_init(drm, irq_se);
if (ret) { if (ret) {
malidp_de_irq_fini(drm); malidp_de_irq_fini(hwdev);
return ret; return ret;
} }
...@@ -326,6 +347,106 @@ static int malidp_dumb_create(struct drm_file *file_priv, ...@@ -326,6 +347,106 @@ static int malidp_dumb_create(struct drm_file *file_priv,
return drm_gem_cma_dumb_create_internal(file_priv, drm, args); return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
} }
#ifdef CONFIG_DEBUG_FS
static void malidp_error_stats_init(struct malidp_error_stats *error_stats)
{
error_stats->num_errors = 0;
error_stats->last_error_status = 0;
error_stats->last_error_vblank = -1;
}
void malidp_error(struct malidp_drm *malidp,
struct malidp_error_stats *error_stats, u32 status,
u64 vblank)
{
unsigned long irqflags;
spin_lock_irqsave(&malidp->errors_lock, irqflags);
error_stats->last_error_status = status;
error_stats->last_error_vblank = vblank;
error_stats->num_errors++;
spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
}
void malidp_error_stats_dump(const char *prefix,
struct malidp_error_stats error_stats,
struct seq_file *m)
{
seq_printf(m, "[%s] num_errors : %d\n", prefix,
error_stats.num_errors);
seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix,
error_stats.last_error_status);
seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix,
error_stats.last_error_vblank);
}
static int malidp_show_stats(struct seq_file *m, void *arg)
{
struct drm_device *drm = m->private;
struct malidp_drm *malidp = drm->dev_private;
unsigned long irqflags;
struct malidp_error_stats de_errors, se_errors;
spin_lock_irqsave(&malidp->errors_lock, irqflags);
de_errors = malidp->de_errors;
se_errors = malidp->se_errors;
spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
malidp_error_stats_dump("DE", de_errors, m);
malidp_error_stats_dump("SE", se_errors, m);
return 0;
}
static int malidp_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, malidp_show_stats, inode->i_private);
}
static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct drm_device *drm = m->private;
struct malidp_drm *malidp = drm->dev_private;
unsigned long irqflags;
spin_lock_irqsave(&malidp->errors_lock, irqflags);
malidp_error_stats_init(&malidp->de_errors);
malidp_error_stats_init(&malidp->se_errors);
spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
return len;
}
static const struct file_operations malidp_debugfs_fops = {
.owner = THIS_MODULE,
.open = malidp_debugfs_open,
.read = seq_read,
.write = malidp_debugfs_write,
.llseek = seq_lseek,
.release = single_release,
};
static int malidp_debugfs_init(struct drm_minor *minor)
{
struct malidp_drm *malidp = minor->dev->dev_private;
struct dentry *dentry = NULL;
malidp_error_stats_init(&malidp->de_errors);
malidp_error_stats_init(&malidp->se_errors);
spin_lock_init(&malidp->errors_lock);
dentry = debugfs_create_file("debug",
S_IRUGO | S_IWUSR,
minor->debugfs_root, minor->dev,
&malidp_debugfs_fops);
if (!dentry) {
DRM_ERROR("Cannot create debug file\n");
return -ENOMEM;
}
return 0;
}
#endif //CONFIG_DEBUG_FS
static struct drm_driver malidp_driver = { static struct drm_driver malidp_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
DRIVER_PRIME, DRIVER_PRIME,
...@@ -342,6 +463,9 @@ static struct drm_driver malidp_driver = { ...@@ -342,6 +463,9 @@ static struct drm_driver malidp_driver = {
.gem_prime_vmap = drm_gem_cma_prime_vmap, .gem_prime_vmap = drm_gem_cma_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap, .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = drm_gem_cma_prime_mmap, .gem_prime_mmap = drm_gem_cma_prime_mmap,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = malidp_debugfs_init,
#endif
.fops = &fops, .fops = &fops,
.name = "mali-dp", .name = "mali-dp",
.desc = "ARM Mali Display Processor driver", .desc = "ARM Mali Display Processor driver",
...@@ -458,6 +582,8 @@ static int malidp_runtime_pm_suspend(struct device *dev) ...@@ -458,6 +582,8 @@ static int malidp_runtime_pm_suspend(struct device *dev)
/* we can only suspend if the hardware is in config mode */ /* we can only suspend if the hardware is in config mode */
WARN_ON(!hwdev->hw->in_config_mode(hwdev)); WARN_ON(!hwdev->hw->in_config_mode(hwdev));
malidp_se_irq_fini(hwdev);
malidp_de_irq_fini(hwdev);
hwdev->pm_suspended = true; hwdev->pm_suspended = true;
clk_disable_unprepare(hwdev->mclk); clk_disable_unprepare(hwdev->mclk);
clk_disable_unprepare(hwdev->aclk); clk_disable_unprepare(hwdev->aclk);
...@@ -476,6 +602,8 @@ static int malidp_runtime_pm_resume(struct device *dev) ...@@ -476,6 +602,8 @@ static int malidp_runtime_pm_resume(struct device *dev)
clk_prepare_enable(hwdev->aclk); clk_prepare_enable(hwdev->aclk);
clk_prepare_enable(hwdev->mclk); clk_prepare_enable(hwdev->mclk);
hwdev->pm_suspended = false; hwdev->pm_suspended = false;
malidp_de_irq_hw_init(hwdev);
malidp_se_irq_hw_init(hwdev);
return 0; return 0;
} }
...@@ -587,8 +715,9 @@ static int malidp_bind(struct device *dev) ...@@ -587,8 +715,9 @@ static int malidp_bind(struct device *dev)
for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) for (i = 0; i < MAX_OUTPUT_CHANNELS; i++)
out_depth = (out_depth << 8) | (output_width[i] & 0xf); out_depth = (out_depth << 8) | (output_width[i] & 0xf);
malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base);
hwdev->output_color_depth = out_depth;
atomic_set(&malidp->config_valid, 0); atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT);
init_waitqueue_head(&malidp->wq); init_waitqueue_head(&malidp->wq);
ret = malidp_init(drm); ret = malidp_init(drm);
...@@ -641,8 +770,8 @@ static int malidp_bind(struct device *dev) ...@@ -641,8 +770,8 @@ static int malidp_bind(struct device *dev)
fbdev_fail: fbdev_fail:
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
vblank_fail: vblank_fail:
malidp_se_irq_fini(drm); malidp_se_irq_fini(hwdev);
malidp_de_irq_fini(drm); malidp_de_irq_fini(hwdev);
drm->irq_enabled = false; drm->irq_enabled = false;
irq_init_fail: irq_init_fail:
drm_atomic_helper_shutdown(drm); drm_atomic_helper_shutdown(drm);
...@@ -672,14 +801,15 @@ static void malidp_unbind(struct device *dev) ...@@ -672,14 +801,15 @@ static void malidp_unbind(struct device *dev)
{ {
struct drm_device *drm = dev_get_drvdata(dev); struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm->dev_private; struct malidp_drm *malidp = drm->dev_private;
struct malidp_hw_device *hwdev = malidp->dev;
drm_dev_unregister(drm); drm_dev_unregister(drm);
drm_fb_cma_fbdev_fini(drm); drm_fb_cma_fbdev_fini(drm);
drm_kms_helper_poll_fini(drm); drm_kms_helper_poll_fini(drm);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
drm_crtc_vblank_off(&malidp->crtc); drm_crtc_vblank_off(&malidp->crtc);
malidp_se_irq_fini(drm); malidp_se_irq_fini(hwdev);
malidp_de_irq_fini(drm); malidp_de_irq_fini(hwdev);
drm->irq_enabled = false; drm->irq_enabled = false;
drm_atomic_helper_shutdown(drm); drm_atomic_helper_shutdown(drm);
component_unbind_all(dev, drm); component_unbind_all(dev, drm);
...@@ -752,8 +882,25 @@ static int __maybe_unused malidp_pm_resume(struct device *dev) ...@@ -752,8 +882,25 @@ static int __maybe_unused malidp_pm_resume(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused malidp_pm_suspend_late(struct device *dev)
{
if (!pm_runtime_status_suspended(dev)) {
malidp_runtime_pm_suspend(dev);
pm_runtime_set_suspended(dev);
}
return 0;
}
static int __maybe_unused malidp_pm_resume_early(struct device *dev)
{
malidp_runtime_pm_resume(dev);
pm_runtime_set_active(dev);
return 0;
}
static const struct dev_pm_ops malidp_pm_ops = { static const struct dev_pm_ops malidp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \
SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \
SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL)
}; };
......
...@@ -13,18 +13,38 @@ ...@@ -13,18 +13,38 @@
#ifndef __MALIDP_DRV_H__ #ifndef __MALIDP_DRV_H__
#define __MALIDP_DRV_H__ #define __MALIDP_DRV_H__
#include <drm/drm_writeback.h>
#include <drm/drm_encoder.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/spinlock.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include "malidp_hw.h" #include "malidp_hw.h"
#define MALIDP_CONFIG_VALID_INIT 0
#define MALIDP_CONFIG_VALID_DONE 1
#define MALIDP_CONFIG_START 0xd0
struct malidp_error_stats {
s32 num_errors;
u32 last_error_status;
s64 last_error_vblank;
};
struct malidp_drm { struct malidp_drm {
struct malidp_hw_device *dev; struct malidp_hw_device *dev;
struct drm_crtc crtc; struct drm_crtc crtc;
struct drm_writeback_connector mw_connector;
wait_queue_head_t wq; wait_queue_head_t wq;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
atomic_t config_valid; atomic_t config_valid;
u32 core_id; u32 core_id;
#ifdef CONFIG_DEBUG_FS
struct malidp_error_stats de_errors;
struct malidp_error_stats se_errors;
/* Protects errors stats */
spinlock_t errors_lock;
#endif
}; };
#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
...@@ -62,6 +82,12 @@ struct malidp_crtc_state { ...@@ -62,6 +82,12 @@ struct malidp_crtc_state {
int malidp_de_planes_init(struct drm_device *drm); int malidp_de_planes_init(struct drm_device *drm);
int malidp_crtc_init(struct drm_device *drm); int malidp_crtc_init(struct drm_device *drm);
#ifdef CONFIG_DEBUG_FS
void malidp_error(struct malidp_drm *malidp,
struct malidp_error_stats *error_stats, u32 status,
u64 vblank);
#endif
/* often used combination of rotational bits */ /* often used combination of rotational bits */
#define MALIDP_ROTATED_MASK (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270) #define MALIDP_ROTATED_MASK (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)
......
This diff is collapsed.
...@@ -33,6 +33,7 @@ enum { ...@@ -33,6 +33,7 @@ enum {
DE_GRAPHICS2 = BIT(2), /* used only in DP500 */ DE_GRAPHICS2 = BIT(2), /* used only in DP500 */
DE_VIDEO2 = BIT(3), DE_VIDEO2 = BIT(3),
DE_SMART = BIT(4), DE_SMART = BIT(4),
SE_MEMWRITE = BIT(5),
}; };
struct malidp_format_id { struct malidp_format_id {
...@@ -52,6 +53,7 @@ struct malidp_format_id { ...@@ -52,6 +53,7 @@ struct malidp_format_id {
struct malidp_irq_map { struct malidp_irq_map {
u32 irq_mask; /* mask of IRQs that can be enabled in the block */ u32 irq_mask; /* mask of IRQs that can be enabled in the block */
u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */ u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */
u32 err_mask; /* mask of bits that represent errors */
}; };
struct malidp_layer { struct malidp_layer {
...@@ -151,12 +153,13 @@ struct malidp_hw { ...@@ -151,12 +153,13 @@ struct malidp_hw {
bool (*in_config_mode)(struct malidp_hw_device *hwdev); bool (*in_config_mode)(struct malidp_hw_device *hwdev);
/* /*
* Set configuration valid flag for hardware parameters that can * Set/clear configuration valid flag for hardware parameters that can
* be changed outside the configuration mode. Hardware will use * be changed outside the configuration mode to the given value.
* the new settings when config valid is set after the end of the * Hardware will use the new settings when config valid is set,
* current buffer scanout * after the end of the current buffer scanout, and will ignore
* any new values for those parameters if config valid flag is cleared
*/ */
void (*set_config_valid)(struct malidp_hw_device *hwdev); void (*set_config_valid)(struct malidp_hw_device *hwdev, u8 value);
/* /*
* Set a new mode in hardware. Requires the hardware to be in * Set a new mode in hardware. Requires the hardware to be in
...@@ -177,6 +180,23 @@ struct malidp_hw { ...@@ -177,6 +180,23 @@ struct malidp_hw {
long (*se_calc_mclk)(struct malidp_hw_device *hwdev, long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
struct malidp_se_config *se_config, struct malidp_se_config *se_config,
struct videomode *vm); struct videomode *vm);
/*
* Enable writing to memory the content of the next frame
* @param hwdev - malidp_hw_device structure containing the HW description
* @param addrs - array of addresses for each plane
* @param pitches - array of pitches for each plane
* @param num_planes - number of planes to be written
* @param w - width of the output frame
* @param h - height of the output frame
* @param fmt_id - internal format ID of output buffer
*/
int (*enable_memwrite)(struct malidp_hw_device *hwdev, dma_addr_t *addrs,
s32 *pitches, int num_planes, u16 w, u16 h, u32 fmt_id);
/*
* Disable the writing to memory of the next frame's content.
*/
void (*disable_memwrite)(struct malidp_hw_device *hwdev);
u8 features; u8 features;
}; };
...@@ -210,10 +230,14 @@ struct malidp_hw_device { ...@@ -210,10 +230,14 @@ struct malidp_hw_device {
u8 min_line_size; u8 min_line_size;
u16 max_line_size; u16 max_line_size;
u32 output_color_depth;
/* track the device PM state */ /* track the device PM state */
bool pm_suspended; bool pm_suspended;
/* track the SE memory writeback state */
u8 mw_state;
/* size of memory used for rotating layers, up to two banks available */ /* size of memory used for rotating layers, up to two banks available */
u32 rotation_memory[2]; u32 rotation_memory[2];
}; };
...@@ -279,9 +303,11 @@ static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev, ...@@ -279,9 +303,11 @@ static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev,
} }
int malidp_de_irq_init(struct drm_device *drm, int irq); int malidp_de_irq_init(struct drm_device *drm, int irq);
void malidp_de_irq_fini(struct drm_device *drm); void malidp_se_irq_hw_init(struct malidp_hw_device *hwdev);
void malidp_de_irq_hw_init(struct malidp_hw_device *hwdev);
void malidp_de_irq_fini(struct malidp_hw_device *hwdev);
int malidp_se_irq_init(struct drm_device *drm, int irq); int malidp_se_irq_init(struct drm_device *drm, int irq);
void malidp_se_irq_fini(struct drm_device *drm); void malidp_se_irq_fini(struct malidp_hw_device *hwdev);
u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
u8 layer_id, u32 format); u8 layer_id, u32 format);
......
// SPDX-License-Identifier: GPL-2.0
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Brian Starkey <brian.starkey@arm.com>
*
* ARM Mali DP Writeback connector implementation
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drmP.h>
#include <drm/drm_writeback.h>
#include "malidp_drv.h"
#include "malidp_hw.h"
#include "malidp_mw.h"
#define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
struct malidp_mw_connector_state {
struct drm_connector_state base;
dma_addr_t addrs[2];
s32 pitches[2];
u8 format;
u8 n_planes;
};
static int malidp_mw_connector_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
return drm_add_modes_noedid(connector, dev->mode_config.max_width,
dev->mode_config.max_height);
}
static enum drm_mode_status
malidp_mw_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct drm_device *dev = connector->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
int w = mode->hdisplay, h = mode->vdisplay;
if ((w < mode_config->min_width) || (w > mode_config->max_width))
return MODE_BAD_HVALUE;
if ((h < mode_config->min_height) || (h > mode_config->max_height))
return MODE_BAD_VVALUE;
return MODE_OK;
}
const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
.get_modes = malidp_mw_connector_get_modes,
.mode_valid = malidp_mw_connector_mode_valid,
};
static void malidp_mw_connector_reset(struct drm_connector *connector)
{
struct malidp_mw_connector_state *mw_state =
kzalloc(sizeof(*mw_state), GFP_KERNEL);
if (connector->state)
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, &mw_state->base);
}
static enum drm_connector_status
malidp_mw_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_disconnected;
}
static void malidp_mw_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
}
static struct drm_connector_state *
malidp_mw_connector_duplicate_state(struct drm_connector *connector)
{
struct malidp_mw_connector_state *mw_state;
if (WARN_ON(!connector->state))
return NULL;
mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL);
if (!mw_state)
return NULL;
/* No need to preserve any of our driver-local data */
__drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base);
return &mw_state->base;
}
static const struct drm_connector_funcs malidp_mw_connector_funcs = {
.reset = malidp_mw_connector_reset,
.detect = malidp_mw_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = malidp_mw_connector_destroy,
.atomic_duplicate_state = malidp_mw_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int
malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state);
struct malidp_drm *malidp = encoder->dev->dev_private;
struct drm_framebuffer *fb;
int i, n_planes;
if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
return 0;
fb = conn_state->writeback_job->fb;
if ((fb->width != crtc_state->mode.hdisplay) ||
(fb->height != crtc_state->mode.vdisplay)) {
DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
fb->width, fb->height);
return -EINVAL;
}
mw_state->format =
malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
fb->format->format);
if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
struct drm_format_name_buf format_name;
DRM_DEBUG_KMS("Invalid pixel format %s\n",
drm_get_format_name(fb->format->format,
&format_name));
return -EINVAL;
}
n_planes = drm_format_num_planes(fb->format->format);
for (i = 0; i < n_planes; i++) {
struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i);
/* memory write buffers are never rotated */
u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0);
if (fb->pitches[i] & (alignment - 1)) {
DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
fb->pitches[i], i);
return -EINVAL;
}
mw_state->pitches[i] = fb->pitches[i];
mw_state->addrs[i] = obj->paddr + fb->offsets[i];
}
mw_state->n_planes = n_planes;
return 0;
}
static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
.atomic_check = malidp_mw_encoder_atomic_check,
};
static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats)
{
const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
u32 *formats;
int n, i;
formats = kcalloc(map->n_pixel_formats, sizeof(*formats),
GFP_KERNEL);
if (!formats)
return NULL;
for (n = 0, i = 0; i < map->n_pixel_formats; i++) {
if (map->pixel_formats[i].layer & SE_MEMWRITE)
formats[n++] = map->pixel_formats[i].format;
}
*n_formats = n;
return formats;
}
int malidp_mw_connector_init(struct drm_device *drm)
{
struct malidp_drm *malidp = drm->dev_private;
u32 *formats;
int ret, n_formats;
if (!malidp->dev->hw->enable_memwrite)
return 0;
malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
drm_connector_helper_add(&malidp->mw_connector.base,
&malidp_mw_connector_helper_funcs);
formats = get_writeback_formats(malidp, &n_formats);
if (!formats)
return -ENOMEM;
ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
&malidp_mw_connector_funcs,
&malidp_mw_encoder_helper_funcs,
formats, n_formats);
kfree(formats);
if (ret)
return ret;
return 0;
}
void malidp_mw_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *old_state)
{
struct malidp_drm *malidp = drm->dev_private;
struct drm_writeback_connector *mw_conn = &malidp->mw_connector;
struct drm_connector_state *conn_state = mw_conn->base.state;
struct malidp_hw_device *hwdev = malidp->dev;
struct malidp_mw_connector_state *mw_state;
if (!conn_state)
return;
mw_state = to_mw_state(conn_state);
if (conn_state->writeback_job && conn_state->writeback_job->fb) {
struct drm_framebuffer *fb = conn_state->writeback_job->fb;
DRM_DEV_DEBUG_DRIVER(drm->dev,
"Enable memwrite %ux%u:%d %pad fmt: %u\n",
fb->width, fb->height,
mw_state->pitches[0],
&mw_state->addrs[0],
mw_state->format);
drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
conn_state->writeback_job = NULL;
hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
mw_state->pitches, mw_state->n_planes,
fb->width, fb->height, mw_state->format);
} else {
DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n");
hwdev->hw->disable_memwrite(hwdev);
}
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
* Author: Brian Starkey <brian.starkey@arm.com>
*
*/
#ifndef __MALIDP_MW_H__
#define __MALIDP_MW_H__
int malidp_mw_connector_init(struct drm_device *drm);
void malidp_mw_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *old_state);
#endif
...@@ -53,6 +53,8 @@ ...@@ -53,6 +53,8 @@
#define MALIDP550_DE_IRQ_AXI_ERR (1 << 16) #define MALIDP550_DE_IRQ_AXI_ERR (1 << 16)
#define MALIDP550_SE_IRQ_EOW (1 << 0) #define MALIDP550_SE_IRQ_EOW (1 << 0)
#define MALIDP550_SE_IRQ_AXI_ERR (1 << 16) #define MALIDP550_SE_IRQ_AXI_ERR (1 << 16)
#define MALIDP550_SE_IRQ_OVR (1 << 17)
#define MALIDP550_SE_IRQ_IBSY (1 << 18)
#define MALIDP550_DC_IRQ_CONF_VALID (1 << 0) #define MALIDP550_DC_IRQ_CONF_VALID (1 << 0)
#define MALIDP550_DC_IRQ_CONF_MODE (1 << 4) #define MALIDP550_DC_IRQ_CONF_MODE (1 << 4)
#define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16) #define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16)
...@@ -60,12 +62,18 @@ ...@@ -60,12 +62,18 @@
#define MALIDP550_DC_IRQ_SE (1 << 24) #define MALIDP550_DC_IRQ_SE (1 << 24)
#define MALIDP650_DE_IRQ_DRIFT (1 << 4) #define MALIDP650_DE_IRQ_DRIFT (1 << 4)
#define MALIDP650_DE_IRQ_ACEV1 (1 << 17)
#define MALIDP650_DE_IRQ_ACEV2 (1 << 18)
#define MALIDP650_DE_IRQ_ACEG (1 << 19)
#define MALIDP650_DE_IRQ_AXIEP (1 << 28)
/* bit masks that are common between products */ /* bit masks that are common between products */
#define MALIDP_CFG_VALID (1 << 0) #define MALIDP_CFG_VALID (1 << 0)
#define MALIDP_DISP_FUNC_GAMMA (1 << 0) #define MALIDP_DISP_FUNC_GAMMA (1 << 0)
#define MALIDP_DISP_FUNC_CADJ (1 << 4) #define MALIDP_DISP_FUNC_CADJ (1 << 4)
#define MALIDP_DISP_FUNC_ILACED (1 << 8) #define MALIDP_DISP_FUNC_ILACED (1 << 8)
#define MALIDP_SCALE_ENGINE_EN (1 << 16)
#define MALIDP_SE_MEMWRITE_EN (2 << 5)
/* register offsets for IRQ management */ /* register offsets for IRQ management */
#define MALIDP_REG_STATUS 0x00000 #define MALIDP_REG_STATUS 0x00000
...@@ -153,6 +161,16 @@ ...@@ -153,6 +161,16 @@
(((x) & MALIDP_SE_ENH_LIMIT_MASK) << 16) (((x) & MALIDP_SE_ENH_LIMIT_MASK) << 16)
#define MALIDP_SE_ENH_COEFF0 0x04 #define MALIDP_SE_ENH_COEFF0 0x04
/* register offsets relative to MALIDP5x0_SE_MEMWRITE_BASE */
#define MALIDP_MW_FORMAT 0x00000
#define MALIDP_MW_P1_STRIDE 0x00004
#define MALIDP_MW_P2_STRIDE 0x00008
#define MALIDP_MW_P1_PTR_LOW 0x0000c
#define MALIDP_MW_P1_PTR_HIGH 0x00010
#define MALIDP_MW_P2_PTR_LOW 0x0002c
#define MALIDP_MW_P2_PTR_HIGH 0x00030
/* register offsets and bits specific to DP500 */ /* register offsets and bits specific to DP500 */
#define MALIDP500_ADDR_SPACE_SIZE 0x01000 #define MALIDP500_ADDR_SPACE_SIZE 0x01000
#define MALIDP500_DC_BASE 0x00000 #define MALIDP500_DC_BASE 0x00000
...@@ -186,7 +204,8 @@ ...@@ -186,7 +204,8 @@
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c #define MALIDP500_DE_LG2_PTR_BASE 0x0031c
#define MALIDP500_SE_BASE 0x00c00 #define MALIDP500_SE_BASE 0x00c00
#define MALIDP500_SE_CONTROL 0x00c0c #define MALIDP500_SE_CONTROL 0x00c0c
#define MALIDP500_SE_PTR_BASE 0x00e0c #define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
#define MALIDP500_SE_MEMWRITE_BASE 0x00e00
#define MALIDP500_DC_IRQ_BASE 0x00f00 #define MALIDP500_DC_IRQ_BASE 0x00f00
#define MALIDP500_CONFIG_VALID 0x00f00 #define MALIDP500_CONFIG_VALID 0x00f00
#define MALIDP500_CONFIG_ID 0x00fd4 #define MALIDP500_CONFIG_ID 0x00fd4
...@@ -217,6 +236,9 @@ ...@@ -217,6 +236,9 @@
#define MALIDP550_DE_PERF_BASE 0x00500 #define MALIDP550_DE_PERF_BASE 0x00500
#define MALIDP550_SE_BASE 0x08000 #define MALIDP550_SE_BASE 0x08000
#define MALIDP550_SE_CONTROL 0x08010 #define MALIDP550_SE_CONTROL 0x08010
#define MALIDP550_SE_MEMWRITE_ONESHOT (1 << 7)
#define MALIDP550_SE_MEMWRITE_OUT_SIZE 0x08030
#define MALIDP550_SE_MEMWRITE_BASE 0x08100
#define MALIDP550_DC_BASE 0x0c000 #define MALIDP550_DC_BASE 0x0c000
#define MALIDP550_DC_CONTROL 0x0c010 #define MALIDP550_DC_CONTROL 0x0c010
#define MALIDP550_DC_CONFIG_REQ (1 << 16) #define MALIDP550_DC_CONFIG_REQ (1 << 16)
......
...@@ -1436,6 +1436,10 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, ...@@ -1436,6 +1436,10 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name);
drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)");
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
if (state->writeback_job && state->writeback_job->fb)
drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id);
if (connector->funcs->atomic_print_state) if (connector->funcs->atomic_print_state)
connector->funcs->atomic_print_state(p, state); connector->funcs->atomic_print_state(p, state);
} }
......
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