Merge tag 'drm-misc-next-2019-10-09-2' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.5: UAPI Changes: -Colorspace: Expose different prop values for DP vs. HDMI (Gwan-gyeong Mun) -fourcc: Add DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED (Raymond) -not_actually: s/ENOTSUPP/EOPNOTSUPP/ in drm_edid and drm_mipi_dbi. This should not reach userspace, but adding here to specifically call that out (Daniel) -i810: Prevent underflow in dispatch ioctls (Dan) -komeda: Add ACLK sysfs attribute (Mihail) -v3d: Allow userspace to clean up after render jobs (Iago) Cross-subsystem Changes: -MAINTAINERS: -Add Alyssa & Steven as panfrost reviewers (Rob) -Add Jernej as DE2 reviewer (Maxime) -Add Chen-Yu as Allwinner maintainer (Maxime) -staging: Make some stack arrays static const (Colin) Core Changes: -ttm: Allow drivers to specify their vma manager (to use gem mgr) (Gerd) -docs: Various fixes in connector/encoder/bridge docs (Daniel, Lyude, Laurent) -connector: Allow more than 3 possible encoders for a connector (José) -dp_cec: Allow a connector to be associated with a cec device (Dariusz) -various: Fix some compile/sparse warnings (Ville) -mm: Ensure mm node removals are properly serialised (Chris) -panel: Specify the type of panel for drm_panels for later use (Laurent) -panel: Use drm_panel_init to init device and funcs (Laurent) -mst: Refactors and cleanups in anticipation of suspend/resume support (Lyude) -vram: -Add lazy unmapping for gem bo's (Thomas) -Unify and rationalize vram mm and gem vram (Thomas) -Expose vmap and vunmap for gem vram objects (Thomas) -Allow objects to be pinned at the top of vram to avoid fragmentation (Thomas) Driver Changes: -various: Include drm_bridge.h instead of relying on drm_crtc.h (Boris) -ast/mgag200: Refactor show_cursor(), move cursor to top of video mem (Thomas) -komeda: -Add error event printing (behind CONFIG) and reg dump support (Lowry) -Add suspend/resume support (Lowry) -Workaround D71 shadow registers not flushing on disable (Lowry) -meson: Add suspend/resume support (Neil) -omap: Miscellaneous refactors and improvements (Tomi/Jyri) -panfrost/shmem: Silence lockdep by using mutex_trylock (Rob) -panfrost: Miscellaneous small fixes (Rob/Steven) -sti: Fix warnings (Benjamin/Linus) -sun4i: -Add vcc-dsi regulator to sun6i_mipi_dsi (Jagan) -A few patches to figure out the DRQ/start delay calc on dsi (Jagan/Icenowy) -virtio: -Add module param to switch resource reuse workaround on/off (Gerd) -Avoid calling vmexit while holding spinlock (Gerd) -Use gem shmem helpers instead of ttm (Gerd) -Accommodate command buffer allocations too big for cma (David) Cc: Rob Herring <robh@kernel.org> Cc: Maxime Ripard <mripard@kernel.org> Cc: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Cc: Lyude Paul <lyude@redhat.com> Cc: José Roberto de Souza <jose.souza@intel.com> Cc: Dariusz Marcinkiewicz <darekm@google.com> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Raymond Smith <raymond.smith@arm.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Colin Ian King <colin.king@canonical.com> Cc: Thomas Zimmermann <tzimmermann@suse.de> Cc: Dan Carpenter <dan.carpenter@oracle.com> Cc: Mihail Atanassov <Mihail.Atanassov@arm.com> Cc: Lowry Li <Lowry.Li@arm.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jyri Sarha <jsarha@ti.com> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com> Cc: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com> Cc: Steven Price <steven.price@arm.com> Cc: Benjamin Gaignard <benjamin.gaignard@st.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Jagan Teki <jagan@amarulasolutions.com> Cc: Icenowy Zheng <icenowy@aosc.io> Cc: Iago Toral Quiroga <itoral@igalia.com> Cc: David Riley <davidriley@chromium.org> Signed-off-by: Dave Airlie <airlied@redhat.com> # gpg: Signature made Thu 10 Oct 2019 01:00:47 AM AEST # gpg: using RSA key 732C002572DCAF79 # gpg: Can't check signature: public key not found # Conflicts: # drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c # drivers/gpu/drm/i915/i915_drv.c # drivers/gpu/drm/i915/i915_gem.c # drivers/gpu/drm/i915/i915_gem_gtt.c # drivers/gpu/drm/i915/i915_vma.c From: Sean Paul <sean@poorly.run> Link: https://patchwork.freedesktop.org/patch/msgid/20191009150825.GA227673@art_vandelay
This commit is contained in:
@@ -12,3 +12,9 @@ config DRM_KOMEDA
|
||||
Processor driver. It supports the D71 variants of the hardware.
|
||||
|
||||
If compiled as a module it will be called komeda.
|
||||
|
||||
config DRM_KOMEDA_ERROR_PRINT
|
||||
bool "Enable komeda error print"
|
||||
depends on DRM_KOMEDA
|
||||
help
|
||||
Choose this option to enable error printing.
|
||||
|
@@ -22,4 +22,6 @@ komeda-y += \
|
||||
d71/d71_dev.o \
|
||||
d71/d71_component.o
|
||||
|
||||
komeda-$(CONFIG_DRM_KOMEDA_ERROR_PRINT) += komeda_event.o
|
||||
|
||||
obj-$(CONFIG_DRM_KOMEDA) += komeda.o
|
||||
|
@@ -1218,6 +1218,90 @@ int d71_probe_block(struct d71_dev *d71,
|
||||
return err;
|
||||
}
|
||||
|
||||
static void d71_gcu_dump(struct d71_dev *d71, struct seq_file *sf)
|
||||
{
|
||||
u32 v[5];
|
||||
|
||||
seq_puts(sf, "\n------ GCU ------\n");
|
||||
|
||||
get_values_from_reg(d71->gcu_addr, 0, 3, v);
|
||||
seq_printf(sf, "GLB_ARCH_ID:\t\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "GLB_CORE_ID:\t\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "GLB_CORE_INFO:\t\t0x%X\n", v[2]);
|
||||
|
||||
get_values_from_reg(d71->gcu_addr, 0x10, 1, v);
|
||||
seq_printf(sf, "GLB_IRQ_STATUS:\t\t0x%X\n", v[0]);
|
||||
|
||||
get_values_from_reg(d71->gcu_addr, 0xA0, 5, v);
|
||||
seq_printf(sf, "GCU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "GCU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "GCU_IRQ_MASK:\t\t0x%X\n", v[2]);
|
||||
seq_printf(sf, "GCU_IRQ_STATUS:\t\t0x%X\n", v[3]);
|
||||
seq_printf(sf, "GCU_STATUS:\t\t0x%X\n", v[4]);
|
||||
|
||||
get_values_from_reg(d71->gcu_addr, 0xD0, 3, v);
|
||||
seq_printf(sf, "GCU_CONTROL:\t\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "GCU_CONFIG_VALID0:\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "GCU_CONFIG_VALID1:\t0x%X\n", v[2]);
|
||||
}
|
||||
|
||||
static void d71_lpu_dump(struct d71_pipeline *pipe, struct seq_file *sf)
|
||||
{
|
||||
u32 v[6];
|
||||
|
||||
seq_printf(sf, "\n------ LPU%d ------\n", pipe->base.id);
|
||||
|
||||
dump_block_header(sf, pipe->lpu_addr);
|
||||
|
||||
get_values_from_reg(pipe->lpu_addr, 0xA0, 6, v);
|
||||
seq_printf(sf, "LPU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "LPU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "LPU_IRQ_MASK:\t\t0x%X\n", v[2]);
|
||||
seq_printf(sf, "LPU_IRQ_STATUS:\t\t0x%X\n", v[3]);
|
||||
seq_printf(sf, "LPU_STATUS:\t\t0x%X\n", v[4]);
|
||||
seq_printf(sf, "LPU_TBU_STATUS:\t\t0x%X\n", v[5]);
|
||||
|
||||
get_values_from_reg(pipe->lpu_addr, 0xC0, 1, v);
|
||||
seq_printf(sf, "LPU_INFO:\t\t0x%X\n", v[0]);
|
||||
|
||||
get_values_from_reg(pipe->lpu_addr, 0xD0, 3, v);
|
||||
seq_printf(sf, "LPU_RAXI_CONTROL:\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "LPU_WAXI_CONTROL:\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "LPU_TBU_CONTROL:\t0x%X\n", v[2]);
|
||||
}
|
||||
|
||||
static void d71_dou_dump(struct d71_pipeline *pipe, struct seq_file *sf)
|
||||
{
|
||||
u32 v[5];
|
||||
|
||||
seq_printf(sf, "\n------ DOU%d ------\n", pipe->base.id);
|
||||
|
||||
dump_block_header(sf, pipe->dou_addr);
|
||||
|
||||
get_values_from_reg(pipe->dou_addr, 0xA0, 5, v);
|
||||
seq_printf(sf, "DOU_IRQ_RAW_STATUS:\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "DOU_IRQ_CLEAR:\t\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "DOU_IRQ_MASK:\t\t0x%X\n", v[2]);
|
||||
seq_printf(sf, "DOU_IRQ_STATUS:\t\t0x%X\n", v[3]);
|
||||
seq_printf(sf, "DOU_STATUS:\t\t0x%X\n", v[4]);
|
||||
}
|
||||
|
||||
static void d71_pipeline_dump(struct komeda_pipeline *pipe, struct seq_file *sf)
|
||||
{
|
||||
struct d71_pipeline *d71_pipe = to_d71_pipeline(pipe);
|
||||
|
||||
d71_lpu_dump(d71_pipe, sf);
|
||||
d71_dou_dump(d71_pipe, sf);
|
||||
}
|
||||
|
||||
const struct komeda_pipeline_funcs d71_pipeline_funcs = {
|
||||
.downscaling_clk_check = d71_downscaling_clk_check,
|
||||
.downscaling_clk_check = d71_downscaling_clk_check,
|
||||
.dump_register = d71_pipeline_dump,
|
||||
};
|
||||
|
||||
void d71_dump(struct komeda_dev *mdev, struct seq_file *sf)
|
||||
{
|
||||
struct d71_dev *d71 = mdev->chip_data;
|
||||
|
||||
d71_gcu_dump(d71, sf);
|
||||
}
|
||||
|
@@ -195,7 +195,7 @@ d71_irq_handler(struct komeda_dev *mdev, struct komeda_events *evts)
|
||||
if (gcu_status & GLB_IRQ_STATUS_PIPE1)
|
||||
evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status);
|
||||
|
||||
return gcu_status ? IRQ_HANDLED : IRQ_NONE;
|
||||
return IRQ_RETVAL(gcu_status);
|
||||
}
|
||||
|
||||
#define ENABLED_GCU_IRQS (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \
|
||||
@@ -395,6 +395,22 @@ static int d71_enum_resources(struct komeda_dev *mdev)
|
||||
err = PTR_ERR(pipe);
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
/* D71 HW doesn't update shadow registers when display output
|
||||
* is turning off, so when we disable all pipeline components
|
||||
* together with display output disable by one flush or one
|
||||
* operation, the disable operation updated registers will not
|
||||
* be flush to or valid in HW, which may leads problem.
|
||||
* To workaround this problem, introduce a two phase disable.
|
||||
* Phase1: Disabling components with display is on to make sure
|
||||
* the disable can be flushed to HW.
|
||||
* Phase2: Only turn-off display output.
|
||||
*/
|
||||
value = KOMEDA_PIPELINE_IMPROCS |
|
||||
BIT(KOMEDA_COMPONENT_TIMING_CTRLR);
|
||||
|
||||
pipe->standalone_disabled_comps = value;
|
||||
|
||||
d71->pipes[i] = to_d71_pipeline(pipe);
|
||||
}
|
||||
|
||||
@@ -561,17 +577,18 @@ static int d71_disconnect_iommu(struct komeda_dev *mdev)
|
||||
}
|
||||
|
||||
static const struct komeda_dev_funcs d71_chip_funcs = {
|
||||
.init_format_table = d71_init_fmt_tbl,
|
||||
.enum_resources = d71_enum_resources,
|
||||
.cleanup = d71_cleanup,
|
||||
.irq_handler = d71_irq_handler,
|
||||
.enable_irq = d71_enable_irq,
|
||||
.disable_irq = d71_disable_irq,
|
||||
.on_off_vblank = d71_on_off_vblank,
|
||||
.change_opmode = d71_change_opmode,
|
||||
.flush = d71_flush,
|
||||
.connect_iommu = d71_connect_iommu,
|
||||
.disconnect_iommu = d71_disconnect_iommu,
|
||||
.init_format_table = d71_init_fmt_tbl,
|
||||
.enum_resources = d71_enum_resources,
|
||||
.cleanup = d71_cleanup,
|
||||
.irq_handler = d71_irq_handler,
|
||||
.enable_irq = d71_enable_irq,
|
||||
.disable_irq = d71_disable_irq,
|
||||
.on_off_vblank = d71_on_off_vblank,
|
||||
.change_opmode = d71_change_opmode,
|
||||
.flush = d71_flush,
|
||||
.connect_iommu = d71_connect_iommu,
|
||||
.disconnect_iommu = d71_disconnect_iommu,
|
||||
.dump_register = d71_dump,
|
||||
};
|
||||
|
||||
const struct komeda_dev_funcs *
|
||||
|
@@ -49,4 +49,6 @@ 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);
|
||||
|
||||
void d71_dump(struct komeda_dev *mdev, struct seq_file *sf);
|
||||
|
||||
#endif /* !_D71_DEV_H_ */
|
||||
|
@@ -5,7 +5,6 @@
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
@@ -250,23 +249,57 @@ komeda_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
{
|
||||
komeda_crtc_prepare(to_kcrtc(crtc));
|
||||
drm_crtc_vblank_on(crtc);
|
||||
WARN_ON(drm_crtc_vblank_get(crtc));
|
||||
komeda_crtc_do_flush(crtc, old);
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc,
|
||||
struct completion *input_flip_done)
|
||||
{
|
||||
struct drm_device *drm = kcrtc->base.dev;
|
||||
struct komeda_dev *mdev = kcrtc->master->mdev;
|
||||
struct completion *flip_done;
|
||||
struct completion temp;
|
||||
int timeout;
|
||||
|
||||
/* if caller doesn't send a flip_done, use a private flip_done */
|
||||
if (input_flip_done) {
|
||||
flip_done = input_flip_done;
|
||||
} else {
|
||||
init_completion(&temp);
|
||||
kcrtc->disable_done = &temp;
|
||||
flip_done = &temp;
|
||||
}
|
||||
|
||||
mdev->funcs->flush(mdev, kcrtc->master->id, 0);
|
||||
|
||||
/* wait the flip take affect.*/
|
||||
timeout = wait_for_completion_timeout(flip_done, HZ);
|
||||
if (timeout == 0) {
|
||||
DRM_ERROR("wait pipe%d flip done timeout\n", kcrtc->master->id);
|
||||
if (!input_flip_done) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drm->event_lock, flags);
|
||||
kcrtc->disable_done = NULL;
|
||||
spin_unlock_irqrestore(&drm->event_lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old)
|
||||
{
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_crtc_state *old_st = to_kcrtc_st(old);
|
||||
struct komeda_dev *mdev = crtc->dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct komeda_pipeline *slave = kcrtc->slave;
|
||||
struct completion *disable_done = &crtc->state->commit->flip_done;
|
||||
struct completion temp;
|
||||
int timeout;
|
||||
bool needs_phase2 = false;
|
||||
|
||||
DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x.\n",
|
||||
DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x\n",
|
||||
drm_crtc_index(crtc),
|
||||
old_st->active_pipes, old_st->affected_pipes);
|
||||
|
||||
@@ -274,7 +307,7 @@ komeda_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
komeda_pipeline_disable(slave, old->state);
|
||||
|
||||
if (has_bit(master->id, old_st->active_pipes))
|
||||
komeda_pipeline_disable(master, old->state);
|
||||
needs_phase2 = komeda_pipeline_disable(master, old->state);
|
||||
|
||||
/* crtc_disable has two scenarios according to the state->active switch.
|
||||
* 1. active -> inactive
|
||||
@@ -293,32 +326,23 @@ komeda_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
* That's also the reason why skip modeset commit in
|
||||
* komeda_crtc_atomic_flush()
|
||||
*/
|
||||
if (crtc->state->active) {
|
||||
struct komeda_pipeline_state *pipe_st;
|
||||
/* clear the old active_comps to zero */
|
||||
pipe_st = komeda_pipeline_get_old_state(master, old->state);
|
||||
pipe_st->active_comps = 0;
|
||||
disable_done = (needs_phase2 || crtc->state->active) ?
|
||||
NULL : &crtc->state->commit->flip_done;
|
||||
|
||||
init_completion(&temp);
|
||||
kcrtc->disable_done = &temp;
|
||||
disable_done = &temp;
|
||||
}
|
||||
|
||||
mdev->funcs->flush(mdev, master->id, 0);
|
||||
|
||||
/* wait the disable take affect.*/
|
||||
timeout = wait_for_completion_timeout(disable_done, HZ);
|
||||
if (timeout == 0) {
|
||||
DRM_ERROR("disable pipeline%d timeout.\n", kcrtc->master->id);
|
||||
if (crtc->state->active) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
kcrtc->disable_done = NULL;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
/* wait phase 1 disable done */
|
||||
komeda_crtc_flush_and_wait_for_flip_done(kcrtc, disable_done);
|
||||
|
||||
/* phase 2 */
|
||||
if (needs_phase2) {
|
||||
komeda_pipeline_disable(kcrtc->master, old->state);
|
||||
|
||||
disable_done = crtc->state->active ?
|
||||
NULL : &crtc->state->commit->flip_done;
|
||||
|
||||
komeda_crtc_flush_and_wait_for_flip_done(kcrtc, disable_done);
|
||||
}
|
||||
|
||||
drm_crtc_vblank_put(crtc);
|
||||
drm_crtc_vblank_off(crtc);
|
||||
komeda_crtc_unprepare(kcrtc);
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ static int komeda_register_show(struct seq_file *sf, void *x)
|
||||
struct komeda_dev *mdev = sf->private;
|
||||
int i;
|
||||
|
||||
seq_puts(sf, "\n====== Komeda register dump =========\n");
|
||||
|
||||
if (mdev->funcs->dump_register)
|
||||
mdev->funcs->dump_register(mdev, sf);
|
||||
|
||||
@@ -91,9 +93,19 @@ config_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
static DEVICE_ATTR_RO(config_id);
|
||||
|
||||
static ssize_t
|
||||
aclk_hz_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct komeda_dev *mdev = dev_to_mdev(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(mdev->aclk));
|
||||
}
|
||||
static DEVICE_ATTR_RO(aclk_hz);
|
||||
|
||||
static struct attribute *komeda_sysfs_entries[] = {
|
||||
&dev_attr_core_id.attr,
|
||||
&dev_attr_config_id.attr,
|
||||
&dev_attr_aclk_hz.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -216,7 +228,7 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
||||
product->product_id,
|
||||
MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id));
|
||||
err = -ENODEV;
|
||||
goto err_cleanup;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
DRM_INFO("Found ARM Mali-D%x version r%dp%d\n",
|
||||
@@ -229,19 +241,19 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
||||
err = mdev->funcs->enum_resources(mdev);
|
||||
if (err) {
|
||||
DRM_ERROR("enumerate display resource failed.\n");
|
||||
goto err_cleanup;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
err = komeda_parse_dt(dev, mdev);
|
||||
if (err) {
|
||||
DRM_ERROR("parse device tree failed.\n");
|
||||
goto err_cleanup;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
err = komeda_assemble_pipelines(mdev);
|
||||
if (err) {
|
||||
DRM_ERROR("assemble display pipelines failed.\n");
|
||||
goto err_cleanup;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
dev->dma_parms = &mdev->dma_parms;
|
||||
@@ -254,11 +266,14 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
||||
if (mdev->iommu && mdev->funcs->connect_iommu) {
|
||||
err = mdev->funcs->connect_iommu(mdev);
|
||||
if (err) {
|
||||
DRM_ERROR("connect iommu failed.\n");
|
||||
mdev->iommu = NULL;
|
||||
goto err_cleanup;
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mdev->aclk);
|
||||
|
||||
err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group);
|
||||
if (err) {
|
||||
DRM_ERROR("create sysfs group failed.\n");
|
||||
@@ -271,6 +286,8 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
||||
|
||||
return mdev;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(mdev->aclk);
|
||||
err_cleanup:
|
||||
komeda_dev_destroy(mdev);
|
||||
return ERR_PTR(err);
|
||||
@@ -288,8 +305,12 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
|
||||
debugfs_remove_recursive(mdev->debugfs_root);
|
||||
#endif
|
||||
|
||||
if (mdev->aclk)
|
||||
clk_prepare_enable(mdev->aclk);
|
||||
|
||||
if (mdev->iommu && mdev->funcs->disconnect_iommu)
|
||||
mdev->funcs->disconnect_iommu(mdev);
|
||||
if (mdev->funcs->disconnect_iommu(mdev))
|
||||
DRM_ERROR("disconnect iommu failed.\n");
|
||||
mdev->iommu = NULL;
|
||||
|
||||
for (i = 0; i < mdev->n_pipelines; i++) {
|
||||
@@ -317,3 +338,47 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
|
||||
|
||||
devm_kfree(dev, mdev);
|
||||
}
|
||||
|
||||
int komeda_dev_resume(struct komeda_dev *mdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
clk_prepare_enable(mdev->aclk);
|
||||
|
||||
if (mdev->iommu && mdev->funcs->connect_iommu) {
|
||||
ret = mdev->funcs->connect_iommu(mdev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("connect iommu failed.\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mdev->funcs->enable_irq(mdev);
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(mdev->aclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int komeda_dev_suspend(struct komeda_dev *mdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
clk_prepare_enable(mdev->aclk);
|
||||
|
||||
if (mdev->iommu && mdev->funcs->disconnect_iommu) {
|
||||
ret = mdev->funcs->disconnect_iommu(mdev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("disconnect iommu failed.\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mdev->funcs->disable_irq(mdev);
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(mdev->aclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -40,6 +40,17 @@
|
||||
#define KOMEDA_ERR_TTNG BIT_ULL(30)
|
||||
#define KOMEDA_ERR_TTF BIT_ULL(31)
|
||||
|
||||
#define KOMEDA_ERR_EVENTS \
|
||||
(KOMEDA_EVENT_URUN | KOMEDA_EVENT_IBSY | KOMEDA_EVENT_OVR |\
|
||||
KOMEDA_ERR_TETO | KOMEDA_ERR_TEMR | KOMEDA_ERR_TITR |\
|
||||
KOMEDA_ERR_CPE | KOMEDA_ERR_CFGE | KOMEDA_ERR_AXIE |\
|
||||
KOMEDA_ERR_ACE0 | KOMEDA_ERR_ACE1 | KOMEDA_ERR_ACE2 |\
|
||||
KOMEDA_ERR_ACE3 | KOMEDA_ERR_DRIFTTO | KOMEDA_ERR_FRAMETO |\
|
||||
KOMEDA_ERR_ZME | KOMEDA_ERR_MERR | KOMEDA_ERR_TCF |\
|
||||
KOMEDA_ERR_TTNG | KOMEDA_ERR_TTF)
|
||||
|
||||
#define KOMEDA_WARN_EVENTS KOMEDA_ERR_CSCE
|
||||
|
||||
/* malidp device id */
|
||||
enum {
|
||||
MALI_D71 = 0,
|
||||
@@ -207,4 +218,13 @@ void komeda_dev_destroy(struct komeda_dev *mdev);
|
||||
|
||||
struct komeda_dev *dev_to_mdev(struct device *dev);
|
||||
|
||||
#ifdef CONFIG_DRM_KOMEDA_ERROR_PRINT
|
||||
void komeda_print_events(struct komeda_events *evts);
|
||||
#else
|
||||
static inline void komeda_print_events(struct komeda_events *evts) {}
|
||||
#endif
|
||||
|
||||
int komeda_dev_resume(struct komeda_dev *mdev);
|
||||
int komeda_dev_suspend(struct komeda_dev *mdev);
|
||||
|
||||
#endif /*_KOMEDA_DEV_H_*/
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
@@ -136,13 +137,40 @@ static const struct of_device_id komeda_of_match[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, komeda_of_match);
|
||||
|
||||
static int __maybe_unused komeda_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct komeda_drv *mdrv = dev_get_drvdata(dev);
|
||||
struct drm_device *drm = &mdrv->kms->base;
|
||||
int res;
|
||||
|
||||
res = drm_mode_config_helper_suspend(drm);
|
||||
|
||||
komeda_dev_suspend(mdrv->mdev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __maybe_unused komeda_pm_resume(struct device *dev)
|
||||
{
|
||||
struct komeda_drv *mdrv = dev_get_drvdata(dev);
|
||||
struct drm_device *drm = &mdrv->kms->base;
|
||||
|
||||
komeda_dev_resume(mdrv->mdev);
|
||||
|
||||
return drm_mode_config_helper_resume(drm);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops komeda_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(komeda_pm_suspend, komeda_pm_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver komeda_platform_driver = {
|
||||
.probe = komeda_platform_probe,
|
||||
.remove = komeda_platform_remove,
|
||||
.driver = {
|
||||
.name = "komeda",
|
||||
.of_match_table = komeda_of_match,
|
||||
.pm = NULL,
|
||||
.pm = &komeda_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
140
drivers/gpu/drm/arm/display/komeda/komeda_event.c
Normal file
140
drivers/gpu/drm/arm/display/komeda/komeda_event.c
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
* Author: James.Qian.Wang <james.qian.wang@arm.com>
|
||||
*
|
||||
*/
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "komeda_dev.h"
|
||||
|
||||
struct komeda_str {
|
||||
char *str;
|
||||
u32 sz;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
/* return 0 on success, < 0 on no space.
|
||||
*/
|
||||
static int komeda_sprintf(struct komeda_str *str, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int num, free_sz;
|
||||
int err;
|
||||
|
||||
free_sz = str->sz - str->len - 1;
|
||||
if (free_sz <= 0)
|
||||
return -ENOSPC;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
num = vsnprintf(str->str + str->len, free_sz, fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (num < free_sz) {
|
||||
str->len += num;
|
||||
err = 0;
|
||||
} else {
|
||||
str->len = str->sz - 1;
|
||||
err = -ENOSPC;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void evt_sprintf(struct komeda_str *str, u64 evt, const char *msg)
|
||||
{
|
||||
if (evt)
|
||||
komeda_sprintf(str, msg);
|
||||
}
|
||||
|
||||
static void evt_str(struct komeda_str *str, u64 events)
|
||||
{
|
||||
if (events == 0ULL) {
|
||||
komeda_sprintf(str, "None");
|
||||
return;
|
||||
}
|
||||
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_VSYNC, "VSYNC|");
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_FLIP, "FLIP|");
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_EOW, "EOW|");
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_MODE, "OP-MODE|");
|
||||
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_URUN, "UNDERRUN|");
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_OVR, "OVERRUN|");
|
||||
|
||||
/* GLB error */
|
||||
evt_sprintf(str, events & KOMEDA_ERR_MERR, "MERR|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");
|
||||
|
||||
/* DOU error */
|
||||
evt_sprintf(str, events & KOMEDA_ERR_DRIFTTO, "DRIFTTO|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TETO, "TETO|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_CSCE, "CSCE|");
|
||||
|
||||
/* LPU errors or events */
|
||||
evt_sprintf(str, events & KOMEDA_EVENT_IBSY, "IBSY|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_AXIE, "AXIE|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_ACE0, "ACE0|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_ACE1, "ACE1|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_ACE2, "ACE2|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_ACE3, "ACE3|");
|
||||
|
||||
/* LPU TBU errors*/
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TCF, "TCF|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TTNG, "TTNG|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TITR, "TITR|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TTF, "TTF|");
|
||||
|
||||
/* CU errors*/
|
||||
evt_sprintf(str, events & KOMEDA_ERR_CPE, "COPROC|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_ZME, "ZME|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_CFGE, "CFGE|");
|
||||
evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");
|
||||
|
||||
if (str->len > 0 && (str->str[str->len - 1] == '|')) {
|
||||
str->str[str->len - 1] = 0;
|
||||
str->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_new_frame(struct komeda_events *a)
|
||||
{
|
||||
return (a->pipes[0] | a->pipes[1]) &
|
||||
(KOMEDA_EVENT_FLIP | KOMEDA_EVENT_EOW);
|
||||
}
|
||||
|
||||
void komeda_print_events(struct komeda_events *evts)
|
||||
{
|
||||
u64 print_evts = KOMEDA_ERR_EVENTS;
|
||||
static bool en_print = true;
|
||||
|
||||
/* reduce the same msg print, only print the first evt for one frame */
|
||||
if (evts->global || is_new_frame(evts))
|
||||
en_print = true;
|
||||
if (!en_print)
|
||||
return;
|
||||
|
||||
if ((evts->global | evts->pipes[0] | evts->pipes[1]) & print_evts) {
|
||||
char msg[256];
|
||||
struct komeda_str str;
|
||||
|
||||
str.str = msg;
|
||||
str.sz = sizeof(msg);
|
||||
str.len = 0;
|
||||
|
||||
komeda_sprintf(&str, "gcu: ");
|
||||
evt_str(&str, evts->global);
|
||||
komeda_sprintf(&str, ", pipes[0]: ");
|
||||
evt_str(&str, evts->pipes[0]);
|
||||
komeda_sprintf(&str, ", pipes[1]: ");
|
||||
evt_str(&str, evts->pipes[1]);
|
||||
|
||||
DRM_ERROR("err detect: %s\n", msg);
|
||||
|
||||
en_print = false;
|
||||
}
|
||||
}
|
@@ -48,6 +48,8 @@ static irqreturn_t komeda_kms_irq_handler(int irq, void *data)
|
||||
memset(&evts, 0, sizeof(evts));
|
||||
status = mdev->funcs->irq_handler(mdev, &evts);
|
||||
|
||||
komeda_print_events(&evts);
|
||||
|
||||
/* Notify the crtc to handle the events */
|
||||
for (i = 0; i < kms->n_crtcs; i++)
|
||||
komeda_crtc_handle_event(&kms->crtcs[i], &evts);
|
||||
|
@@ -389,6 +389,18 @@ struct komeda_pipeline {
|
||||
int id;
|
||||
/** @avail_comps: available components mask of pipeline */
|
||||
u32 avail_comps;
|
||||
/**
|
||||
* @standalone_disabled_comps:
|
||||
*
|
||||
* When disable the pipeline, some components can not be disabled
|
||||
* together with others, but need a sparated and standalone disable.
|
||||
* The standalone_disabled_comps are the components which need to be
|
||||
* disabled standalone, and this concept also introduce concept of
|
||||
* two phase.
|
||||
* phase 1: for disabling the common components.
|
||||
* phase 2: for disabling the standalong_disabled_comps.
|
||||
*/
|
||||
u32 standalone_disabled_comps;
|
||||
/** @n_layers: the number of layer on @layers */
|
||||
int n_layers;
|
||||
/** @layers: the pipeline layers */
|
||||
@@ -535,7 +547,7 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
|
||||
struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_old_state(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state);
|
||||
void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
bool komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state);
|
||||
void komeda_pipeline_update(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state);
|
||||
|
@@ -1218,7 +1218,17 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
/* Since standalong disabled components must be disabled separately and in the
|
||||
* last, So a complete disable operation may needs to call pipeline_disable
|
||||
* twice (two phase disabling).
|
||||
* Phase 1: disable the common components, flush it.
|
||||
* Phase 2: disable the standalone disabled components, flush it.
|
||||
*
|
||||
* RETURNS:
|
||||
* true: disable is not complete, needs a phase 2 disable.
|
||||
* false: disable is complete.
|
||||
*/
|
||||
bool komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct komeda_pipeline_state *old;
|
||||
@@ -1228,9 +1238,14 @@ void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
|
||||
old = komeda_pipeline_get_old_state(pipe, old_state);
|
||||
|
||||
disabling_comps = old->active_comps;
|
||||
DRM_DEBUG_ATOMIC("PIPE%d: disabling_comps: 0x%x.\n",
|
||||
pipe->id, disabling_comps);
|
||||
disabling_comps = old->active_comps &
|
||||
(~pipe->standalone_disabled_comps);
|
||||
if (!disabling_comps)
|
||||
disabling_comps = old->active_comps &
|
||||
pipe->standalone_disabled_comps;
|
||||
|
||||
DRM_DEBUG_ATOMIC("PIPE%d: active_comps: 0x%x, disabling_comps: 0x%x.\n",
|
||||
pipe->id, old->active_comps, disabling_comps);
|
||||
|
||||
dp_for_each_set_bit(id, disabling_comps) {
|
||||
c = komeda_pipeline_get_component(pipe, id);
|
||||
@@ -1248,6 +1263,13 @@ void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
|
||||
c->funcs->disable(c);
|
||||
}
|
||||
|
||||
/* Update the pipeline state, if there are components that are still
|
||||
* active, return true for calling the phase 2 disable.
|
||||
*/
|
||||
old->active_comps &= ~disabling_comps;
|
||||
|
||||
return old->active_comps ? true : false;
|
||||
}
|
||||
|
||||
void komeda_pipeline_update(struct komeda_pipeline *pipe,
|
||||
|
Reference in New Issue
Block a user