select SHMEM
select TMPFS
select QCOM_SCM if ARCH_QCOM
+ select QCOM_COMMAND_DB if ARCH_QCOM
select WANT_DEV_COREDUMP
select SND_SOC_HDMI_CODEC if SND_SOC
select SYNC_FILE
select PM_OPP
- default y
help
DRM/KMS driver for MSM/snapdragon.
disp/dpu1/dpu_rm.o \
disp/dpu1/dpu_vbif.o \
msm_atomic.o \
+ msm_atomic_tracepoints.o \
msm_debugfs.o \
msm_drv.o \
msm_fb.o \
/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
*/
-
#include <linux/types.h>
#include <linux/debugfs.h>
+
+#include <drm/drm_debugfs.h>
+#include <drm/drm_file.h>
#include <drm/drm_print.h>
#include "a5xx_gpu.h"
static int a6xx_gmu_clocks_probe(struct a6xx_gmu *gmu)
{
- int ret = msm_clk_bulk_get(gmu->dev, &gmu->clocks);
+ int ret = devm_clk_bulk_get_all(gmu->dev, &gmu->clocks);
if (ret < 1)
return ret;
MODULE_FIRMWARE("qcom/a530_zap.b02");
MODULE_FIRMWARE("qcom/a630_sqe.fw");
MODULE_FIRMWARE("qcom/a630_gmu.bin");
+MODULE_FIRMWARE("qcom/a630_zap.mbn");
static inline bool _rev_match(uint8_t entry, uint8_t id)
{
*/
void dpu_core_perf_crtc_release_bw(struct drm_crtc *crtc)
{
- struct drm_crtc *tmp_crtc;
struct dpu_crtc *dpu_crtc;
struct dpu_crtc_state *dpu_cstate;
struct dpu_kms *kms;
dpu_crtc = to_dpu_crtc(crtc);
dpu_cstate = to_dpu_crtc_state(crtc->state);
- /* only do this for command mode rt client */
- if (dpu_crtc_get_intf_mode(crtc) != INTF_MODE_CMD)
+ if (atomic_dec_return(&kms->bandwidth_ref) > 0)
return;
- /*
- * If video interface present, cmd panel bandwidth cannot be
- * released.
- */
- if (dpu_crtc_get_intf_mode(crtc) == INTF_MODE_CMD)
- drm_for_each_crtc(tmp_crtc, crtc->dev) {
- if (tmp_crtc->enabled &&
- dpu_crtc_get_intf_mode(tmp_crtc) ==
- INTF_MODE_VIDEO)
- return;
- }
-
/* Release the bandwidth */
if (kms->perf.enable_bw_release) {
trace_dpu_cmd_release_bw(crtc->base.id);
#include <linux/sort.h>
#include <linux/debugfs.h>
#include <linux/ktime.h>
+
#include <drm/drm_crtc.h>
#include <drm/drm_flip_work.h>
#include <drm/drm_mode.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_rect.h>
+#include <drm/drm_vblank.h>
#include "dpu_kms.h"
#include "dpu_hw_lm.h"
trace_dpu_crtc_vblank_cb(DRMID(crtc));
}
-static void dpu_crtc_release_bw_unlocked(struct drm_crtc *crtc)
-{
- int ret = 0;
- struct drm_modeset_acquire_ctx ctx;
-
- DRM_MODESET_LOCK_ALL_BEGIN(crtc->dev, ctx, 0, ret);
- dpu_core_perf_crtc_release_bw(crtc);
- DRM_MODESET_LOCK_ALL_END(ctx, ret);
- if (ret)
- DRM_ERROR("Failed to acquire modeset locks to release bw, %d\n",
- ret);
-}
-
static void dpu_crtc_frame_event_work(struct kthread_work *work)
{
struct dpu_crtc_frame_event *fevent = container_of(work,
| DPU_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
if (atomic_read(&dpu_crtc->frame_pending) < 1) {
- /* this should not happen */
- DRM_ERROR("crtc%d ev:%u ts:%lld frame_pending:%d\n",
- crtc->base.id,
- fevent->event,
- ktime_to_ns(fevent->ts),
- atomic_read(&dpu_crtc->frame_pending));
+ /* ignore vblank when not pending */
} else if (atomic_dec_return(&dpu_crtc->frame_pending) == 0) {
/* release bandwidth and other resources */
trace_dpu_crtc_frame_event_done(DRMID(crtc),
fevent->event);
- dpu_crtc_release_bw_unlocked(crtc);
+ dpu_core_perf_crtc_release_bw(crtc);
} else {
trace_dpu_crtc_frame_event_more_pending(DRMID(crtc),
fevent->event);
kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
}
-void dpu_crtc_complete_commit(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state)
+void dpu_crtc_complete_commit(struct drm_crtc *crtc)
{
- if (!crtc || !crtc->state) {
- DPU_ERROR("invalid crtc\n");
- return;
- }
trace_dpu_crtc_complete_commit(DRMID(crtc));
}
return rc;
}
-void dpu_crtc_commit_kickoff(struct drm_crtc *crtc, bool async)
+void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
{
struct drm_encoder *encoder;
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc);
struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
- int ret;
/*
* If no mixers has been allocated in dpu_crtc_atomic_check(),
*/
drm_for_each_encoder_mask(encoder, crtc->dev,
crtc->state->encoder_mask)
- dpu_encoder_prepare_for_kickoff(encoder, async);
-
- if (!async) {
- /* wait for frame_event_done completion */
- DPU_ATRACE_BEGIN("wait_for_frame_done_event");
- ret = _dpu_crtc_wait_for_frame_done(crtc);
- DPU_ATRACE_END("wait_for_frame_done_event");
- if (ret) {
- DPU_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
- crtc->base.id,
- atomic_read(&dpu_crtc->frame_pending));
- goto end;
- }
+ dpu_encoder_prepare_for_kickoff(encoder);
- if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) {
- /* acquire bandwidth and other resources */
- DPU_DEBUG("crtc%d first commit\n", crtc->base.id);
- } else
- DPU_DEBUG("crtc%d commit\n", crtc->base.id);
+ if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) {
+ /* acquire bandwidth and other resources */
+ DPU_DEBUG("crtc%d first commit\n", crtc->base.id);
+ } else
+ DPU_DEBUG("crtc%d commit\n", crtc->base.id);
- dpu_crtc->play_count++;
- }
+ dpu_crtc->play_count++;
dpu_vbif_clear_errors(dpu_kms);
drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
- dpu_encoder_kickoff(encoder, async);
+ dpu_encoder_kickoff(encoder);
-end:
- if (!async)
- reinit_completion(&dpu_crtc->frame_done_comp);
+ reinit_completion(&dpu_crtc->frame_done_comp);
DPU_ATRACE_END("crtc_commit");
}
struct drm_encoder *encoder;
struct msm_drm_private *priv;
unsigned long flags;
+ bool release_bandwidth = false;
if (!crtc || !crtc->dev || !crtc->dev->dev_private || !crtc->state) {
DPU_ERROR("invalid crtc\n");
drm_crtc_vblank_off(crtc);
drm_for_each_encoder_mask(encoder, crtc->dev,
- old_crtc_state->encoder_mask)
+ old_crtc_state->encoder_mask) {
+ /* in video mode, we hold an extra bandwidth reference
+ * as we cannot drop bandwidth at frame-done if any
+ * crtc is being used in video mode.
+ */
+ if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
+ release_bandwidth = true;
dpu_encoder_assign_crtc(encoder, NULL);
+ }
/* wait for frame_event_done completion */
if (_dpu_crtc_wait_for_frame_done(crtc))
if (atomic_read(&dpu_crtc->frame_pending)) {
trace_dpu_crtc_disable_frame_pending(DRMID(crtc),
atomic_read(&dpu_crtc->frame_pending));
- dpu_core_perf_crtc_release_bw(crtc);
+ if (release_bandwidth)
+ dpu_core_perf_crtc_release_bw(crtc);
atomic_set(&dpu_crtc->frame_pending, 0);
}
struct dpu_crtc *dpu_crtc;
struct drm_encoder *encoder;
struct msm_drm_private *priv;
+ bool request_bandwidth;
if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
DPU_ERROR("invalid crtc\n");
DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
dpu_crtc = to_dpu_crtc(crtc);
- drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
+ drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) {
+ /* in video mode, we hold an extra bandwidth reference
+ * as we cannot drop bandwidth at frame-done if any
+ * crtc is being used in video mode.
+ */
+ if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
+ request_bandwidth = true;
dpu_encoder_register_frame_event_callback(encoder,
dpu_crtc_frame_event_cb, (void *)crtc);
+ }
+
+ if (request_bandwidth)
+ atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref);
trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
dpu_crtc->enabled = true;
}
}
+ atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref);
+
rc = dpu_core_perf_crtc_check(crtc, state);
if (rc) {
DPU_ERROR("crtc%d failed performance check %d\n",
/**
* dpu_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
* @crtc: Pointer to drm crtc object
- * @async: true if the commit is asynchronous, false otherwise
*/
-void dpu_crtc_commit_kickoff(struct drm_crtc *crtc, bool async);
+void dpu_crtc_commit_kickoff(struct drm_crtc *crtc);
/**
* dpu_crtc_complete_commit - callback signalling completion of current commit
* @crtc: Pointer to drm crtc object
- * @old_state: Pointer to drm crtc old state object
*/
-void dpu_crtc_complete_commit(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state);
+void dpu_crtc_complete_commit(struct drm_crtc *crtc);
/**
* dpu_crtc_init - create a new crtc object
*/
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
-#include <linux/kthread.h>
#include <linux/debugfs.h>
+#include <linux/kthread.h>
#include <linux/seq_file.h>
-#include "msm_drv.h"
-#include "dpu_kms.h"
#include <drm/drm_crtc.h>
+#include <drm/drm_file.h>
#include <drm/drm_probe_helper.h>
+
+#include "msm_drv.h"
+#include "dpu_kms.h"
#include "dpu_hwio.h"
#include "dpu_hw_catalog.h"
#include "dpu_hw_intf.h"
* extra_flush_bits: Additional bit mask to include in flush trigger
*/
static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc,
- struct dpu_encoder_phys *phys, uint32_t extra_flush_bits,
- bool async)
+ struct dpu_encoder_phys *phys, uint32_t extra_flush_bits)
{
struct dpu_hw_ctl *ctl;
int pending_kickoff_cnt;
u32 ret = UINT_MAX;
- if (!drm_enc || !phys) {
- DPU_ERROR("invalid argument(s), drm_enc %d, phys_enc %d\n",
- drm_enc != 0, phys != 0);
- return;
- }
-
if (!phys->hw_pp) {
DPU_ERROR("invalid pingpong hw\n");
return;
return;
}
- if (!async)
- pending_kickoff_cnt = dpu_encoder_phys_inc_pending(phys);
- else
- pending_kickoff_cnt = atomic_read(&phys->pending_kickoff_cnt);
+ pending_kickoff_cnt = dpu_encoder_phys_inc_pending(phys);
if (extra_flush_bits && ctl->ops.update_pending_flush)
ctl->ops.update_pending_flush(ctl, extra_flush_bits);
* a time.
* dpu_enc: Pointer to virtual encoder structure
*/
-static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc,
- bool async)
+static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc)
{
struct dpu_hw_ctl *ctl;
uint32_t i, pending_flush;
unsigned long lock_flags;
- if (!dpu_enc) {
- DPU_ERROR("invalid encoder\n");
- return;
- }
-
pending_flush = 0x0;
/* update pending counts and trigger kickoff ctl flush atomically */
* for async commits. So don't set this for async, since it'll
* roll over to the next commit.
*/
- if (!async && phys->split_role != ENC_ROLE_SLAVE)
+ if (phys->split_role != ENC_ROLE_SLAVE)
set_bit(i, dpu_enc->frame_busy_mask);
if (!phys->ops.needs_single_flush ||
!phys->ops.needs_single_flush(phys))
- _dpu_encoder_trigger_flush(&dpu_enc->base, phys, 0x0,
- async);
+ _dpu_encoder_trigger_flush(&dpu_enc->base, phys, 0x0);
else if (ctl->ops.get_pending_flush)
pending_flush |= ctl->ops.get_pending_flush(ctl);
}
_dpu_encoder_trigger_flush(
&dpu_enc->base,
dpu_enc->cur_master,
- pending_flush, async);
+ pending_flush);
}
_dpu_encoder_trigger_start(dpu_enc->cur_master);
return line_time;
}
-static int _dpu_encoder_wakeup_time(struct drm_encoder *drm_enc,
- ktime_t *wakeup_time)
+int dpu_encoder_vsync_time(struct drm_encoder *drm_enc, ktime_t *wakeup_time)
{
struct drm_display_mode *mode;
struct dpu_encoder_virt *dpu_enc;
return;
}
- if (_dpu_encoder_wakeup_time(&dpu_enc->base, &wakeup_time))
+ if (dpu_encoder_vsync_time(&dpu_enc->base, &wakeup_time))
return;
trace_dpu_enc_vsync_event_work(DRMID(&dpu_enc->base), wakeup_time);
nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
}
-void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, bool async)
+void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
{
struct dpu_encoder_virt *dpu_enc;
struct dpu_encoder_phys *phys;
bool needs_hw_reset = false;
unsigned int i;
- if (!drm_enc) {
- DPU_ERROR("invalid args\n");
- return;
- }
dpu_enc = to_dpu_encoder_virt(drm_enc);
trace_dpu_enc_prepare_kickoff(DRMID(drm_enc));
}
}
-void dpu_encoder_kickoff(struct drm_encoder *drm_enc, bool async)
+void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
{
struct dpu_encoder_virt *dpu_enc;
struct dpu_encoder_phys *phys;
ktime_t wakeup_time;
+ unsigned long timeout_ms;
unsigned int i;
- if (!drm_enc) {
- DPU_ERROR("invalid encoder\n");
- return;
- }
DPU_ATRACE_BEGIN("encoder_kickoff");
dpu_enc = to_dpu_encoder_virt(drm_enc);
trace_dpu_enc_kickoff(DRMID(drm_enc));
- /*
- * Asynchronous frames don't handle FRAME_DONE events. As such, they
- * shouldn't enable the frame_done watchdog since it will always time
- * out.
- */
- if (!async) {
- unsigned long timeout_ms;
- timeout_ms = DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES * 1000 /
+ timeout_ms = DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES * 1000 /
drm_mode_vrefresh(&drm_enc->crtc->state->adjusted_mode);
- atomic_set(&dpu_enc->frame_done_timeout_ms, timeout_ms);
- mod_timer(&dpu_enc->frame_done_timer,
- jiffies + msecs_to_jiffies(timeout_ms));
- }
+ atomic_set(&dpu_enc->frame_done_timeout_ms, timeout_ms);
+ mod_timer(&dpu_enc->frame_done_timer,
+ jiffies + msecs_to_jiffies(timeout_ms));
/* All phys encs are ready to go, trigger the kickoff */
- _dpu_encoder_kickoff_phys(dpu_enc, async);
+ _dpu_encoder_kickoff_phys(dpu_enc);
/* allow phys encs to handle any post-kickoff business */
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
}
if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_DSI &&
- !_dpu_encoder_wakeup_time(drm_enc, &wakeup_time)) {
+ !dpu_encoder_vsync_time(drm_enc, &wakeup_time)) {
trace_dpu_enc_early_kickoff(DRMID(drm_enc),
ktime_to_ms(wakeup_time));
mod_timer(&dpu_enc->vsync_event_timer,
* Immediately: if no previous commit is outstanding.
* Delayed: Block until next trigger can be issued.
* @encoder: encoder pointer
- * @async: true if this is an asynchronous commit
*/
-void dpu_encoder_prepare_for_kickoff(struct drm_encoder *encoder, bool async);
+void dpu_encoder_prepare_for_kickoff(struct drm_encoder *encoder);
/**
* dpu_encoder_trigger_kickoff_pending - Clear the flush bits from previous
* dpu_encoder_kickoff - trigger a double buffer flip of the ctl path
* (i.e. ctl flush and start) immediately.
* @encoder: encoder pointer
- * @async: true if this is an asynchronous commit
*/
-void dpu_encoder_kickoff(struct drm_encoder *encoder, bool async);
+void dpu_encoder_kickoff(struct drm_encoder *encoder);
+
+/**
+ * dpu_encoder_wakeup_time - get the time of the next vsync
+ */
+int dpu_encoder_vsync_time(struct drm_encoder *drm_enc, ktime_t *wakeup_time);
/**
* dpu_encoder_wait_for_event - Waits for encoder events
static void dpu_encoder_phys_cmd_handle_post_kickoff(
struct dpu_encoder_phys *phys_enc)
{
- if (!phys_enc)
- return;
-
/**
* re-enable external TE, either for the first time after enabling
* or if disabled for Autorefresh
/* Signal any waiting atomic commit thread */
wake_up_all(&phys_enc->pending_kickoff_wq);
+
+ phys_enc->parent_ops->handle_frame_done(phys_enc->parent, phys_enc,
+ DPU_ENCODER_FRAME_EVENT_DONE);
+
DPU_ATRACE_END("vblank_irq");
}
hw_res->intfs[phys_enc->intf_idx - INTF_0] = INTF_MODE_VIDEO;
}
-static int _dpu_encoder_phys_vid_wait_for_vblank(
- struct dpu_encoder_phys *phys_enc, bool notify)
+static int dpu_encoder_phys_vid_wait_for_vblank(
+ struct dpu_encoder_phys *phys_enc)
{
struct dpu_encoder_wait_info wait_info;
int ret;
wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
if (!dpu_encoder_phys_vid_is_master(phys_enc)) {
- if (notify && phys_enc->parent_ops->handle_frame_done)
- phys_enc->parent_ops->handle_frame_done(
- phys_enc->parent, phys_enc,
- DPU_ENCODER_FRAME_EVENT_DONE);
return 0;
}
if (ret == -ETIMEDOUT) {
dpu_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_VSYNC);
- } else if (!ret && notify && phys_enc->parent_ops->handle_frame_done)
- phys_enc->parent_ops->handle_frame_done(
- phys_enc->parent, phys_enc,
- DPU_ENCODER_FRAME_EVENT_DONE);
+ }
return ret;
}
-static int dpu_encoder_phys_vid_wait_for_vblank(
+static int dpu_encoder_phys_vid_wait_for_commit_done(
struct dpu_encoder_phys *phys_enc)
{
- return _dpu_encoder_phys_vid_wait_for_vblank(phys_enc, true);
+ struct dpu_hw_ctl *hw_ctl = phys_enc->hw_ctl;
+ int ret;
+
+ if (!hw_ctl)
+ return 0;
+
+ ret = wait_event_timeout(phys_enc->pending_kickoff_wq,
+ (hw_ctl->ops.get_flush_register(hw_ctl) == 0),
+ msecs_to_jiffies(50));
+ if (ret <= 0) {
+ DPU_ERROR("vblank timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
}
static void dpu_encoder_phys_vid_prepare_for_kickoff(
* scanout buffer) don't latch properly..
*/
if (dpu_encoder_phys_vid_is_master(phys_enc)) {
- ret = _dpu_encoder_phys_vid_wait_for_vblank(phys_enc, false);
+ ret = dpu_encoder_phys_vid_wait_for_vblank(phys_enc);
if (ret) {
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
DRM_ERROR("wait disable failed: id:%u intf:%d ret:%d\n",
{
unsigned long lock_flags;
- if (!phys_enc) {
- DPU_ERROR("invalid encoder\n");
- return;
- }
-
/*
* Video mode must flush CTL before enabling timing engine
* Video encoders need to turn on their interfaces now
ops->destroy = dpu_encoder_phys_vid_destroy;
ops->get_hw_resources = dpu_encoder_phys_vid_get_hw_resources;
ops->control_vblank_irq = dpu_encoder_phys_vid_control_vblank_irq;
- ops->wait_for_commit_done = dpu_encoder_phys_vid_wait_for_vblank;
+ ops->wait_for_commit_done = dpu_encoder_phys_vid_wait_for_commit_done;
ops->wait_for_vblank = dpu_encoder_phys_vid_wait_for_vblank;
ops->wait_for_tx_complete = dpu_encoder_phys_vid_wait_for_vblank;
ops->irq_control = dpu_encoder_phys_vid_irq_control;
#include <linux/bug.h>
#include <linux/bitmap.h>
#include <linux/err.h>
-#include <drm/drmP.h>
/**
* Max hardware block count: For ex: max 12 SSPP pipes or
static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx)
{
- if (!ctx)
- return 0x0;
-
return ctx->pending_flush_mask;
}
#include <linux/clk/clk-conf.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <drm/drm_print.h>
#ifndef __DPU_IO_UTIL_H__
#define __DPU_IO_UTIL_H__
-#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#define DEV_WARN(fmt, args...) pr_warn(fmt, ##args)
#define DEV_ERR(fmt, args...) pr_err(fmt, ##args)
-struct dss_gpio {
- unsigned int gpio;
- unsigned int value;
- char gpio_name[32];
-};
-
enum dss_clk_type {
DSS_CLK_AHB, /* no set rate. rate controlled through rpm */
DSS_CLK_PCLK,
};
struct dss_module_power {
- unsigned int num_gpio;
- struct dss_gpio *gpio_config;
unsigned int num_clk;
struct dss_clk *clk_config;
};
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
-#include <drm/drm_crtc.h>
#include <linux/debugfs.h>
-#include <linux/of_irq.h>
#include <linux/dma-buf.h>
+#include <linux/of_irq.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_file.h>
#include "msm_drv.h"
#include "msm_mmu.h"
dpu_crtc_vblank(crtc, false);
}
+static void dpu_kms_enable_commit(struct msm_kms *kms)
+{
+ struct dpu_kms *dpu_kms = to_dpu_kms(kms);
+ pm_runtime_get_sync(&dpu_kms->pdev->dev);
+}
+
+static void dpu_kms_disable_commit(struct msm_kms *kms)
+{
+ struct dpu_kms *dpu_kms = to_dpu_kms(kms);
+ pm_runtime_put_sync(&dpu_kms->pdev->dev);
+}
+
+static ktime_t dpu_kms_vsync_time(struct msm_kms *kms, struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+
+ drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) {
+ ktime_t vsync_time;
+
+ if (dpu_encoder_vsync_time(encoder, &vsync_time) == 0)
+ return vsync_time;
+ }
+
+ return ktime_get();
+}
+
static void dpu_kms_prepare_commit(struct msm_kms *kms,
struct drm_atomic_state *state)
{
if (!dev || !dev->dev_private)
return;
priv = dev->dev_private;
- pm_runtime_get_sync(&dpu_kms->pdev->dev);
/* Call prepare_commit for all affected encoders */
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
}
}
+static void dpu_kms_flush_commit(struct msm_kms *kms, unsigned crtc_mask)
+{
+ struct dpu_kms *dpu_kms = to_dpu_kms(kms);
+ struct drm_crtc *crtc;
+
+ for_each_crtc_mask(dpu_kms->dev, crtc, crtc_mask) {
+ if (!crtc->state->active)
+ continue;
+
+ trace_dpu_kms_commit(DRMID(crtc));
+ dpu_crtc_commit_kickoff(crtc);
+ }
+}
+
/*
* Override the encoder enable since we need to setup the inline rotator and do
* some crtc magic before enabling any bridge that might be present.
continue;
trace_dpu_kms_enc_enable(DRMID(crtc));
- dpu_crtc_commit_kickoff(crtc, false);
- }
-}
-
-static void dpu_kms_commit(struct msm_kms *kms, struct drm_atomic_state *state)
-{
- struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state;
- int i;
-
- for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
- /* If modeset is required, kickoff is run in encoder_enable */
- if (drm_atomic_crtc_needs_modeset(crtc_state))
- continue;
-
- if (crtc->state->active) {
- trace_dpu_kms_commit(DRMID(crtc));
- dpu_crtc_commit_kickoff(crtc,
- state->legacy_cursor_update);
- }
}
}
-static void dpu_kms_complete_commit(struct msm_kms *kms,
- struct drm_atomic_state *old_state)
+static void dpu_kms_complete_commit(struct msm_kms *kms, unsigned crtc_mask)
{
- struct dpu_kms *dpu_kms;
- struct msm_drm_private *priv;
+ struct dpu_kms *dpu_kms = to_dpu_kms(kms);
struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state;
- int i;
-
- if (!kms || !old_state)
- return;
- dpu_kms = to_dpu_kms(kms);
-
- if (!dpu_kms->dev || !dpu_kms->dev->dev_private)
- return;
- priv = dpu_kms->dev->dev_private;
DPU_ATRACE_BEGIN("kms_complete_commit");
- for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i)
- dpu_crtc_complete_commit(crtc, old_crtc_state);
-
- pm_runtime_put_sync(&dpu_kms->pdev->dev);
+ for_each_crtc_mask(dpu_kms->dev, crtc, crtc_mask)
+ dpu_crtc_complete_commit(crtc);
DPU_ATRACE_END("kms_complete_commit");
}
}
}
+static void dpu_kms_wait_flush(struct msm_kms *kms, unsigned crtc_mask)
+{
+ struct dpu_kms *dpu_kms = to_dpu_kms(kms);
+ struct drm_crtc *crtc;
+
+ for_each_crtc_mask(dpu_kms->dev, crtc, crtc_mask)
+ dpu_kms_wait_for_commit_done(kms, crtc);
+}
+
static int _dpu_kms_initialize_dsi(struct drm_device *dev,
struct msm_drm_private *priv,
struct dpu_kms *dpu_kms)
int primary_planes_idx = 0, cursor_planes_idx = 0, i, ret;
int max_crtc_count;
- if (!dpu_kms || !dpu_kms->dev || !dpu_kms->dev->dev) {
- DPU_ERROR("invalid dpu_kms\n");
- return -EINVAL;
- }
-
dev = dpu_kms->dev;
priv = dev->dev_private;
catalog = dpu_kms->catalog;
.irq_preinstall = dpu_irq_preinstall,
.irq_uninstall = dpu_irq_uninstall,
.irq = dpu_irq,
+ .enable_commit = dpu_kms_enable_commit,
+ .disable_commit = dpu_kms_disable_commit,
+ .vsync_time = dpu_kms_vsync_time,
.prepare_commit = dpu_kms_prepare_commit,
- .commit = dpu_kms_commit,
+ .flush_commit = dpu_kms_flush_commit,
+ .wait_flush = dpu_kms_wait_flush,
.complete_commit = dpu_kms_complete_commit,
- .wait_for_crtc_commit_done = dpu_kms_wait_for_commit_done,
.enable_vblank = dpu_kms_enable_vblank,
.disable_vblank = dpu_kms_disable_vblank,
.check_modified_format = dpu_format_check_modified_format,
return rc;
}
+ atomic_set(&dpu_kms->bandwidth_ref, 0);
+
dpu_kms->mmio = msm_ioremap(dpu_kms->pdev, "mdp", "mdp");
if (IS_ERR(dpu_kms->mmio)) {
rc = PTR_ERR(dpu_kms->mmio);
#ifndef __DPU_KMS_H__
#define __DPU_KMS_H__
+#include <drm/drm_drv.h>
+
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_mmu.h"
struct platform_device *pdev;
bool rpm_enabled;
struct dss_module_power mp;
+
+ /* reference count bandwidth requests, so we know when we can
+ * release bandwidth. Each atomic update increments, and frame-
+ * done event decrements. Additionally, for video mode, the
+ * reference is incremented when crtc is enabled, and decremented
+ * when disabled.
+ */
+ atomic_t bandwidth_ref;
};
struct vsync_info {
* Copyright (c) 2018, The Linux Foundation
*/
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdesc.h>
+#include <linux/irqchip/chained_irq.h>
#include "dpu_kms.h"
#include <linux/interconnect.h>
struct msm_mdss base;
void __iomem *mmio;
unsigned long mmio_len;
- u32 hwversion;
struct dss_module_power mp;
struct dpu_irq_controller irq_controller;
struct icc_path *path[2];
dpu_mdss_icc_request_bw(priv->mdss);
- pm_runtime_get_sync(dev->dev);
- dpu_mdss->hwversion = readl_relaxed(dpu_mdss->mmio);
- pm_runtime_put_sync(dev->dev);
-
return ret;
irq_error:
#include <linux/debugfs.h>
#include <linux/dma-buf.h>
-#include <drm/drm_damage_helper.h>
#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_file.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include "msm_drv.h"
pstate->multirect_mode);
if (pdpu->pipe_hw->ops.setup_format) {
+ unsigned int rotation;
+
src_flags = 0x0;
+ rotation = drm_rotation_simplify(state->rotation,
+ DRM_MODE_ROTATE_0 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y);
+
+ if (rotation & DRM_MODE_REFLECT_X)
+ src_flags |= DPU_SSPP_FLIP_LR;
+
+ if (rotation & DRM_MODE_REFLECT_Y)
+ src_flags |= DPU_SSPP_FLIP_UD;
+
/* update format */
pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, fmt, src_flags,
pstate->multirect_index);
if (ret)
DPU_ERROR("failed to install zpos property, rc = %d\n", ret);
+ drm_plane_create_rotation_property(plane,
+ DRM_MODE_ROTATE_0,
+ DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y);
+
drm_plane_enable_fb_damage_clips(plane);
/* success! finalize initialization */
__entry->rc_state = rc_state;
__assign_str(stage_str, stage);
),
- TP_printk("%s: id:%u, sw_event:%d, idle_pc_supported:%s, rc_state:%d\n",
+ TP_printk("%s: id:%u, sw_event:%d, idle_pc_supported:%s, rc_state:%d",
__get_str(stage_str), __entry->drm_id, __entry->sw_event,
__entry->idle_pc_supported ? "true" : "false",
__entry->rc_state)
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include "dpu_vbif.h"
#include "dpu_hw_vbif.h"
struct dpu_hw_vbif *vbif;
u32 i, pnd, src;
- if (!dpu_kms) {
- DPU_ERROR("invalid argument\n");
- return;
- }
-
for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) {
vbif = dpu_kms->hw_vbif[i];
if (vbif && vbif->ops.clear_errors) {
struct dpu_hw_vbif *vbif;
int i, j;
- if (!dpu_kms) {
- DPU_ERROR("invalid argument\n");
- return;
- }
-
for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) {
vbif = dpu_kms->hw_vbif[i];
if (vbif && vbif->cap && vbif->ops.set_mem_type) {
#include <drm/drm_flip_work.h>
#include <drm/drm_mode.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
#include "mdp4_kms.h"
*/
#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "mdp4_kms.h"
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/delay.h>
+
+#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "msm_gem.h"
return ret;
}
-static void mdp4_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state)
+static void mdp4_enable_commit(struct msm_kms *kms)
+{
+ struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+ mdp4_enable(mdp4_kms);
+}
+
+static void mdp4_disable_commit(struct msm_kms *kms)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+ mdp4_disable(mdp4_kms);
+}
+
+static void mdp4_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state)
+{
int i;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
- mdp4_enable(mdp4_kms);
-
/* see 119ecb7fd */
for_each_new_crtc_in_state(state, crtc, crtc_state, i)
drm_crtc_vblank_get(crtc);
}
-static void mdp4_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
+static void mdp4_flush_commit(struct msm_kms *kms, unsigned crtc_mask)
+{
+ /* TODO */
+}
+
+static void mdp4_wait_flush(struct msm_kms *kms, unsigned crtc_mask)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
- int i;
struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state;
- drm_atomic_helper_wait_for_vblanks(mdp4_kms->dev, state);
-
- /* see 119ecb7fd */
- for_each_new_crtc_in_state(state, crtc, crtc_state, i)
- drm_crtc_vblank_put(crtc);
-
- mdp4_disable(mdp4_kms);
+ for_each_crtc_mask(mdp4_kms->dev, crtc, crtc_mask)
+ mdp4_crtc_wait_for_commit_done(crtc);
}
-static void mdp4_wait_for_crtc_commit_done(struct msm_kms *kms,
- struct drm_crtc *crtc)
+static void mdp4_complete_commit(struct msm_kms *kms, unsigned crtc_mask)
{
- mdp4_crtc_wait_for_commit_done(crtc);
+ struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+ struct drm_crtc *crtc;
+
+ /* see 119ecb7fd */
+ for_each_crtc_mask(mdp4_kms->dev, crtc, crtc_mask)
+ drm_crtc_vblank_put(crtc);
}
static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
.irq = mdp4_irq,
.enable_vblank = mdp4_enable_vblank,
.disable_vblank = mdp4_disable_vblank,
+ .enable_commit = mdp4_enable_commit,
+ .disable_commit = mdp4_disable_commit,
.prepare_commit = mdp4_prepare_commit,
+ .flush_commit = mdp4_flush_commit,
+ .wait_flush = mdp4_wait_flush,
.complete_commit = mdp4_complete_commit,
- .wait_for_crtc_commit_done = mdp4_wait_for_crtc_commit_done,
.get_format = mdp_get_format,
.round_pixclk = mdp4_round_pixclk,
.destroy = mdp4_destroy,
* Author: Vinay Simha <vinaysimha@inforcecomputing.com>
*/
+#include <linux/delay.h>
+
#include <drm/drm_crtc.h>
#include <drm/drm_probe_helper.h>
* Author: Vinay Simha <vinaysimha@inforcecomputing.com>
*/
-#include <linux/gpio.h>
-
#include "mdp4_kms.h"
struct mdp4_lvds_connector {
*/
#include <drm/drm_damage_helper.h>
+#include <drm/drm_fourcc.h>
+
#include "mdp4_kms.h"
#define DOWN_SCALE_MAX 8
.max_clk = 320000000,
};
-static const struct mdp5_cfg_handler cfg_handlers[] = {
+const struct mdp5_cfg_hw msm8998_config = {
+ .name = "msm8998",
+ .mdp = {
+ .count = 1,
+ .caps = MDP_CAP_DSC |
+ MDP_CAP_CDM |
+ MDP_CAP_SRC_SPLIT |
+ 0,
+ },
+ .ctl = {
+ .count = 5,
+ .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 },
+ .flush_hw_mask = 0xf7ffffff,
+ },
+ .pipe_vig = {
+ .count = 4,
+ .base = { 0x04000, 0x06000, 0x08000, 0x0a000 },
+ .caps = MDP_PIPE_CAP_HFLIP |
+ MDP_PIPE_CAP_VFLIP |
+ MDP_PIPE_CAP_SCALE |
+ MDP_PIPE_CAP_CSC |
+ MDP_PIPE_CAP_DECIMATION |
+ MDP_PIPE_CAP_SW_PIX_EXT |
+ 0,
+ },
+ .pipe_rgb = {
+ .count = 4,
+ .base = { 0x14000, 0x16000, 0x18000, 0x1a000 },
+ .caps = MDP_PIPE_CAP_HFLIP |
+ MDP_PIPE_CAP_VFLIP |
+ MDP_PIPE_CAP_SCALE |
+ MDP_PIPE_CAP_DECIMATION |
+ MDP_PIPE_CAP_SW_PIX_EXT |
+ 0,
+ },
+ .pipe_dma = {
+ .count = 2, /* driver supports max of 2 currently */
+ .base = { 0x24000, 0x26000, 0x28000, 0x2a000 },
+ .caps = MDP_PIPE_CAP_HFLIP |
+ MDP_PIPE_CAP_VFLIP |
+ MDP_PIPE_CAP_SW_PIX_EXT |
+ 0,
+ },
+ .pipe_cursor = {
+ .count = 2,
+ .base = { 0x34000, 0x36000 },
+ .caps = MDP_PIPE_CAP_HFLIP |
+ MDP_PIPE_CAP_VFLIP |
+ MDP_PIPE_CAP_SW_PIX_EXT |
+ MDP_PIPE_CAP_CURSOR |
+ 0,
+ },
+
+ .lm = {
+ .count = 6,
+ .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 },
+ .instances = {
+ { .id = 0, .pp = 0, .dspp = 0,
+ .caps = MDP_LM_CAP_DISPLAY |
+ MDP_LM_CAP_PAIR, },
+ { .id = 1, .pp = 1, .dspp = 1,
+ .caps = MDP_LM_CAP_DISPLAY, },
+ { .id = 2, .pp = 2, .dspp = -1,
+ .caps = MDP_LM_CAP_DISPLAY |
+ MDP_LM_CAP_PAIR, },
+ { .id = 3, .pp = -1, .dspp = -1,
+ .caps = MDP_LM_CAP_WB, },
+ { .id = 4, .pp = -1, .dspp = -1,
+ .caps = MDP_LM_CAP_WB, },
+ { .id = 5, .pp = 3, .dspp = -1,
+ .caps = MDP_LM_CAP_DISPLAY, },
+ },
+ .nb_stages = 8,
+ .max_width = 2560,
+ .max_height = 0xFFFF,
+ },
+ .dspp = {
+ .count = 2,
+ .base = { 0x54000, 0x56000 },
+ },
+ .ad = {
+ .count = 3,
+ .base = { 0x78000, 0x78800, 0x79000 },
+ },
+ .pp = {
+ .count = 4,
+ .base = { 0x70000, 0x70800, 0x71000, 0x71800 },
+ },
+ .cdm = {
+ .count = 1,
+ .base = { 0x79200 },
+ },
+ .dsc = {
+ .count = 2,
+ .base = { 0x80000, 0x80400 },
+ },
+ .intf = {
+ .base = { 0x6a000, 0x6a800, 0x6b000, 0x6b800, 0x6c000 },
+ .connect = {
+ [0] = INTF_eDP,
+ [1] = INTF_DSI,
+ [2] = INTF_DSI,
+ [3] = INTF_HDMI,
+ },
+ },
+ .max_clk = 412500000,
+};
+
+static const struct mdp5_cfg_handler cfg_handlers_v1[] = {
{ .revision = 0, .config = { .hw = &msm8x74v1_config } },
{ .revision = 2, .config = { .hw = &msm8x74v2_config } },
{ .revision = 3, .config = { .hw = &apq8084_config } },
{ .revision = 15, .config = { .hw = &msm8917_config } },
};
+static const struct mdp5_cfg_handler cfg_handlers_v3[] = {
+ { .revision = 0, .config = { .hw = &msm8998_config } },
+};
+
static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev);
const struct mdp5_cfg_hw *mdp5_cfg_get_hw_config(struct mdp5_cfg_handler *cfg_handler)
struct drm_device *dev = mdp5_kms->dev;
struct platform_device *pdev = to_platform_device(dev->dev);
struct mdp5_cfg_handler *cfg_handler;
+ const struct mdp5_cfg_handler *cfg_handlers;
struct mdp5_cfg_platform *pconfig;
- int i, ret = 0;
+ int i, ret = 0, num_handlers;
cfg_handler = kzalloc(sizeof(*cfg_handler), GFP_KERNEL);
if (unlikely(!cfg_handler)) {
goto fail;
}
- if (major != 1) {
+ switch (major) {
+ case 1:
+ cfg_handlers = cfg_handlers_v1;
+ num_handlers = ARRAY_SIZE(cfg_handlers_v1);
+ break;
+ case 3:
+ cfg_handlers = cfg_handlers_v3;
+ num_handlers = ARRAY_SIZE(cfg_handlers_v3);
+ break;
+ default:
DRM_DEV_ERROR(dev->dev, "unexpected MDP major version: v%d.%d\n",
major, minor);
ret = -ENXIO;
goto fail;
- }
+ };
/* only after mdp5_cfg global pointer's init can we access the hw */
- for (i = 0; i < ARRAY_SIZE(cfg_handlers); i++) {
+ for (i = 0; i < num_handlers; i++) {
if (cfg_handlers[i].revision != minor)
continue;
mdp5_cfg = cfg_handlers[i].config.hw;
*/
#include <linux/sort.h>
+
#include <drm/drm_mode.h>
#include <drm/drm_crtc.h>
#include <drm/drm_flip_work.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
#include "mdp5_kms.h"
u32 blend_cfg;
struct mdp5_hw_mixer *mixer = pipeline->mixer;
- if (unlikely(WARN_ON(!mixer))) {
+ if (WARN_ON(!mixer)) {
DRM_DEV_ERROR(ctl_mgr->dev->dev, "CTL %d cannot find LM",
ctl->id);
return -EINVAL;
goto fail;
}
- if (unlikely(WARN_ON(ctl_cfg->count > MAX_CTL))) {
+ if (WARN_ON(ctl_cfg->count > MAX_CTL)) {
DRM_DEV_ERROR(dev->dev, "Increase static pool size to at least %d\n",
ctl_cfg->count);
ret = -ENOSPC;
#include <linux/irq.h>
#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "mdp5_kms.h"
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/delay.h>
#include <linux/interconnect.h>
#include <linux/of_irq.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_vblank.h>
+
#include "msm_drv.h"
#include "msm_gem.h"
#include "msm_mmu.h"
return 0;
}
+static void mdp5_enable_commit(struct msm_kms *kms)
+{
+ struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ pm_runtime_get_sync(&mdp5_kms->pdev->dev);
+}
+
+static void mdp5_disable_commit(struct msm_kms *kms)
+{
+ struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ pm_runtime_put_sync(&mdp5_kms->pdev->dev);
+}
+
static void mdp5_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
- struct device *dev = &mdp5_kms->pdev->dev;
struct mdp5_global_state *global_state;
global_state = mdp5_get_existing_global_state(mdp5_kms);
- pm_runtime_get_sync(dev);
-
if (mdp5_kms->smp)
mdp5_smp_prepare_commit(mdp5_kms->smp, &global_state->smp);
}
-static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
+static void mdp5_flush_commit(struct msm_kms *kms, unsigned crtc_mask)
+{
+ /* TODO */
+}
+
+static void mdp5_wait_flush(struct msm_kms *kms, unsigned crtc_mask)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
- struct device *dev = &mdp5_kms->pdev->dev;
- struct mdp5_global_state *global_state;
+ struct drm_crtc *crtc;
- drm_atomic_helper_wait_for_vblanks(mdp5_kms->dev, state);
+ for_each_crtc_mask(mdp5_kms->dev, crtc, crtc_mask)
+ mdp5_crtc_wait_for_commit_done(crtc);
+}
+
+static void mdp5_complete_commit(struct msm_kms *kms, unsigned crtc_mask)
+{
+ struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ struct mdp5_global_state *global_state;
global_state = mdp5_get_existing_global_state(mdp5_kms);
if (mdp5_kms->smp)
mdp5_smp_complete_commit(mdp5_kms->smp, &global_state->smp);
-
- pm_runtime_put_sync(dev);
-}
-
-static void mdp5_wait_for_crtc_commit_done(struct msm_kms *kms,
- struct drm_crtc *crtc)
-{
- mdp5_crtc_wait_for_commit_done(crtc);
}
static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
.irq = mdp5_irq,
.enable_vblank = mdp5_enable_vblank,
.disable_vblank = mdp5_disable_vblank,
+ .flush_commit = mdp5_flush_commit,
+ .enable_commit = mdp5_enable_commit,
+ .disable_commit = mdp5_disable_commit,
.prepare_commit = mdp5_prepare_commit,
+ .wait_flush = mdp5_wait_flush,
.complete_commit = mdp5_complete_commit,
- .wait_for_crtc_commit_done = mdp5_wait_for_crtc_commit_done,
.get_format = mdp_get_format,
.round_pixclk = mdp5_round_pixclk,
.set_split_display = mdp5_set_split_display,
struct msm_kms *kms;
struct msm_gem_address_space *aspace;
int irq, i, ret;
+ struct device *iommu_dev;
/* priv->kms would have been populated by the MDP5 driver */
kms = priv->kms;
mdelay(16);
if (config->platform.iommu) {
- aspace = msm_gem_address_space_create(&pdev->dev,
+ iommu_dev = &pdev->dev;
+ if (!iommu_dev->iommu_fwspec)
+ iommu_dev = iommu_dev->parent;
+
+ aspace = msm_gem_address_space_create(iommu_dev,
config->platform.iommu, "mdp5");
if (IS_ERR(aspace)) {
ret = PTR_ERR(aspace);
*/
#include <drm/drm_damage_helper.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>
+
#include "mdp5_kms.h"
struct mdp5_plane {
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <drm/drm_fourcc.h>
#include <drm/drm_util.h>
#include "mdp5_kms.h"
struct csc_cfg *mdp_get_default_csc_cfg(enum csc_type type)
{
- if (unlikely(WARN_ON(type >= CSC_MAX)))
+ if (WARN_ON(type >= CSC_MAX))
return NULL;
return &csc_convert[type];
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/of_graph.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spinlock.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
+
#include <video/mipi_display.h>
#include "dsi.h"
}
msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk);
- if (!msm_host->byte_clk_src) {
- ret = -ENODEV;
+ if (IS_ERR(msm_host->byte_clk_src)) {
+ ret = PTR_ERR(msm_host->byte_clk_src);
pr_err("%s: can't find byte_clk clock. ret=%d\n", __func__, ret);
goto exit;
}
msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk);
- if (!msm_host->pixel_clk_src) {
- ret = -ENODEV;
+ if (IS_ERR(msm_host->pixel_clk_src)) {
+ ret = PTR_ERR(msm_host->pixel_clk_src);
pr_err("%s: can't find pixel_clk clock. ret=%d\n", __func__, ret);
goto exit;
}
ret = devm_regulator_bulk_get(dev, num, s);
if (ret < 0) {
- DRM_DEV_ERROR(dev, "%s: failed to init regulator, ret=%d\n",
- __func__, ret);
+ if (ret != -EPROBE_DEFER) {
+ DRM_DEV_ERROR(dev,
+ "%s: failed to init regulator, ret=%d\n",
+ __func__, ret);
+ }
+
return ret;
}
}
ret = dsi_phy_regulator_init(phy);
- if (ret) {
- DRM_DEV_ERROR(dev, "%s: failed to init regulator\n", __func__);
+ if (ret)
goto fail;
- }
phy->ahb_clk = msm_clk_get(pdev, "iface");
if (IS_ERR(phy->ahb_clk)) {
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*/
+#include <linux/delay.h>
+
#include "dsi_phy.h"
#include "dsi.xml.h"
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
+#include <linux/delay.h>
+
#include "dsi_phy.h"
#include "dsi.xml.h"
#ifndef __DSI_PLL_H__
#define __DSI_PLL_H__
-#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include "dsi.h"
{ "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" },
};
-static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name)
-{
- int gpio;
-
- /* try with the gpio names as in the table (downstream bindings) */
- gpio = of_get_named_gpio(of_node, name, 0);
- if (gpio < 0) {
- char name2[32];
-
- /* try with the gpio names as in the upstream bindings */
- snprintf(name2, sizeof(name2), "%s-gpios", name);
- gpio = of_get_named_gpio(of_node, name2, 0);
- if (gpio < 0) {
- char name3[32];
-
- /*
- * try again after stripping out the "qcom,hdmi-tx"
- * prefix. This is mainly to match "hpd-gpios" used
- * in the upstream bindings
- */
- if (sscanf(name2, "qcom,hdmi-tx-%s", name3))
- gpio = of_get_named_gpio(of_node, name3, 0);
- }
-
- if (gpio < 0) {
- DBG("failed to get gpio: %s (%d)", name, gpio);
- gpio = -1;
- }
- }
- return gpio;
-}
-
/*
* HDMI audio codec callbacks
*/
hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
- hdmi_cfg->gpios[i].num = msm_hdmi_get_gpio(of_node,
- msm_hdmi_gpio_pdata[i].name);
+ const char *name = msm_hdmi_gpio_pdata[i].name;
+ struct gpio_desc *gpiod;
+
+ /*
+ * We are fetching the GPIO lines "as is" since the connector
+ * code is enabling and disabling the lines. Until that point
+ * the power-on default value will be kept.
+ */
+ gpiod = devm_gpiod_get_optional(dev, name, GPIOD_ASIS);
+ /* This will catch e.g. -PROBE_DEFER */
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ if (!gpiod) {
+ /* Try a second time, stripping down the name */
+ char name3[32];
+
+ /*
+ * Try again after stripping out the "qcom,hdmi-tx"
+ * prefix. This is mainly to match "hpd-gpios" used
+ * in the upstream bindings.
+ */
+ if (sscanf(name, "qcom,hdmi-tx-%s", name3))
+ gpiod = devm_gpiod_get_optional(dev, name3, GPIOD_ASIS);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ if (!gpiod)
+ DBG("failed to get gpio: %s", name);
+ }
+ hdmi_cfg->gpios[i].gpiod = gpiod;
+ if (gpiod)
+ gpiod_set_consumer_name(gpiod, msm_hdmi_gpio_pdata[i].label);
hdmi_cfg->gpios[i].output = msm_hdmi_gpio_pdata[i].output;
hdmi_cfg->gpios[i].value = msm_hdmi_gpio_pdata[i].value;
- hdmi_cfg->gpios[i].label = msm_hdmi_gpio_pdata[i].label;
}
dev->platform_data = hdmi_cfg;
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
#include <linux/hdmi.h>
#include "msm_drv.h"
struct hdmi_platform_config;
struct hdmi_gpio_data {
- int num;
+ struct gpio_desc *gpiod;
bool output;
int value;
- const char *label;
};
struct hdmi_audio {
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/delay.h>
+
#include "hdmi.h"
struct hdmi_bridge {
* Author: Rob Clark <robdclark@gmail.com>
*/
-#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include "msm_kms.h"
static int gpio_config(struct hdmi *hdmi, bool on)
{
- struct device *dev = &hdmi->pdev->dev;
const struct hdmi_platform_config *config = hdmi->config;
- int ret, i;
+ int i;
if (on) {
for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
struct hdmi_gpio_data gpio = config->gpios[i];
- if (gpio.num != -1) {
- ret = gpio_request(gpio.num, gpio.label);
- if (ret) {
- DRM_DEV_ERROR(dev,
- "'%s'(%d) gpio_request failed: %d\n",
- gpio.label, gpio.num, ret);
- goto err;
- }
-
+ if (gpio.gpiod) {
if (gpio.output) {
- gpio_direction_output(gpio.num,
- gpio.value);
+ gpiod_direction_output(gpio.gpiod,
+ gpio.value);
} else {
- gpio_direction_input(gpio.num);
- gpio_set_value_cansleep(gpio.num,
- gpio.value);
+ gpiod_direction_input(gpio.gpiod);
+ gpiod_set_value_cansleep(gpio.gpiod,
+ gpio.value);
}
}
}
for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
struct hdmi_gpio_data gpio = config->gpios[i];
- if (gpio.num == -1)
+ if (!gpio.gpiod)
continue;
if (gpio.output) {
int value = gpio.value ? 0 : 1;
- gpio_set_value_cansleep(gpio.num, value);
+ gpiod_set_value_cansleep(gpio.gpiod, value);
}
-
- gpio_free(gpio.num);
};
DBG("gpio off");
}
return 0;
-err:
- while (i--) {
- if (config->gpios[i].num != -1)
- gpio_free(config->gpios[i].num);
- }
-
- return ret;
}
static void enable_hpd_clocks(struct hdmi *hdmi, bool enable)
const struct hdmi_platform_config *config = hdmi->config;
struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
- return gpio_get_value(hpd_gpio.num) ?
+ return gpiod_get_value(hpd_gpio.gpiod) ?
connector_status_connected :
connector_status_disconnected;
}
* some platforms may not have hpd gpio. Rely only on the status
* provided by REG_HDMI_HPD_INT_STATUS in this case.
*/
- if (hpd_gpio.num == -1)
+ if (!hpd_gpio.gpiod)
return detect_reg(hdmi);
do {
*/
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include "hdmi.h"
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/delay.h>
+
#include "hdmi.h"
static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy,
*/
#include <linux/clk-provider.h>
+#include <linux/delay.h>
+
#include "hdmi.h"
struct hdmi_pll_8960 {
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_vblank.h>
+#include "msm_atomic_trace.h"
#include "msm_drv.h"
#include "msm_gem.h"
#include "msm_kms.h"
-static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
- struct drm_atomic_state *old_state)
+int msm_atomic_prepare_fb(struct drm_plane *plane,
+ struct drm_plane_state *new_state)
{
- struct drm_crtc *crtc;
- struct drm_crtc_state *new_crtc_state;
- struct msm_drm_private *priv = old_state->dev->dev_private;
+ struct msm_drm_private *priv = plane->dev->dev_private;
struct msm_kms *kms = priv->kms;
- int i;
- for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
- if (!new_crtc_state->active)
- continue;
+ if (!new_state->fb)
+ return 0;
- if (drm_crtc_vblank_get(crtc))
- continue;
+ drm_gem_fb_prepare_fb(plane, new_state);
- kms->funcs->wait_for_crtc_commit_done(kms, crtc);
+ return msm_framebuffer_prepare(new_state->fb, kms->aspace);
+}
- drm_crtc_vblank_put(crtc);
+static void msm_atomic_async_commit(struct msm_kms *kms, int crtc_idx)
+{
+ unsigned crtc_mask = BIT(crtc_idx);
+
+ trace_msm_atomic_async_commit_start(crtc_mask);
+
+ mutex_lock(&kms->commit_lock);
+
+ if (!(kms->pending_crtc_mask & crtc_mask)) {
+ mutex_unlock(&kms->commit_lock);
+ goto out;
}
+
+ kms->pending_crtc_mask &= ~crtc_mask;
+
+ kms->funcs->enable_commit(kms);
+
+ /*
+ * Flush hardware updates:
+ */
+ trace_msm_atomic_flush_commit(crtc_mask);
+ kms->funcs->flush_commit(kms, crtc_mask);
+ mutex_unlock(&kms->commit_lock);
+
+ /*
+ * Wait for flush to complete:
+ */
+ trace_msm_atomic_wait_flush_start(crtc_mask);
+ kms->funcs->wait_flush(kms, crtc_mask);
+ trace_msm_atomic_wait_flush_finish(crtc_mask);
+
+ mutex_lock(&kms->commit_lock);
+ kms->funcs->complete_commit(kms, crtc_mask);
+ mutex_unlock(&kms->commit_lock);
+ kms->funcs->disable_commit(kms);
+
+out:
+ trace_msm_atomic_async_commit_finish(crtc_mask);
}
-int msm_atomic_prepare_fb(struct drm_plane *plane,
- struct drm_plane_state *new_state)
+static enum hrtimer_restart msm_atomic_pending_timer(struct hrtimer *t)
{
- struct msm_drm_private *priv = plane->dev->dev_private;
- struct msm_kms *kms = priv->kms;
+ struct msm_pending_timer *timer = container_of(t,
+ struct msm_pending_timer, timer);
+ struct msm_drm_private *priv = timer->kms->dev->dev_private;
- if (!new_state->fb)
- return 0;
+ queue_work(priv->wq, &timer->work);
- drm_gem_fb_prepare_fb(plane, new_state);
+ return HRTIMER_NORESTART;
+}
- return msm_framebuffer_prepare(new_state->fb, kms->aspace);
+static void msm_atomic_pending_work(struct work_struct *work)
+{
+ struct msm_pending_timer *timer = container_of(work,
+ struct msm_pending_timer, work);
+
+ msm_atomic_async_commit(timer->kms, timer->crtc_idx);
+}
+
+void msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
+ struct msm_kms *kms, int crtc_idx)
+{
+ timer->kms = kms;
+ timer->crtc_idx = crtc_idx;
+ hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ timer->timer.function = msm_atomic_pending_timer;
+ INIT_WORK(&timer->work, msm_atomic_pending_work);
+}
+
+static bool can_do_async(struct drm_atomic_state *state,
+ struct drm_crtc **async_crtc)
+{
+ struct drm_connector_state *connector_state;
+ struct drm_connector *connector;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int i, num_crtcs = 0;
+
+ if (!(state->legacy_cursor_update || state->async_update))
+ return false;
+
+ /* any connector change, means slow path: */
+ for_each_new_connector_in_state(state, connector, connector_state, i)
+ return false;
+
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ if (drm_atomic_crtc_needs_modeset(crtc_state))
+ return false;
+ if (++num_crtcs > 1)
+ return false;
+ *async_crtc = crtc;
+ }
+
+ return true;
+}
+
+/* Get bitmask of crtcs that will need to be flushed. The bitmask
+ * can be used with for_each_crtc_mask() iterator, to iterate
+ * effected crtcs without needing to preserve the atomic state.
+ */
+static unsigned get_crtc_mask(struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ unsigned i, mask = 0;
+
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i)
+ mask |= drm_crtc_mask(crtc);
+
+ return mask;
}
void msm_atomic_commit_tail(struct drm_atomic_state *state)
struct drm_device *dev = state->dev;
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
+ struct drm_crtc *async_crtc = NULL;
+ unsigned crtc_mask = get_crtc_mask(state);
+ bool async = kms->funcs->vsync_time &&
+ can_do_async(state, &async_crtc);
+
+ trace_msm_atomic_commit_tail_start(async, crtc_mask);
+
+ kms->funcs->enable_commit(kms);
+ /*
+ * Ensure any previous (potentially async) commit has
+ * completed:
+ */
+ trace_msm_atomic_wait_flush_start(crtc_mask);
+ kms->funcs->wait_flush(kms, crtc_mask);
+ trace_msm_atomic_wait_flush_finish(crtc_mask);
+
+ mutex_lock(&kms->commit_lock);
+
+ /*
+ * Now that there is no in-progress flush, prepare the
+ * current update:
+ */
kms->funcs->prepare_commit(kms, state);
+ /*
+ * Push atomic updates down to hardware:
+ */
drm_atomic_helper_commit_modeset_disables(dev, state);
-
drm_atomic_helper_commit_planes(dev, state, 0);
-
drm_atomic_helper_commit_modeset_enables(dev, state);
- if (kms->funcs->commit) {
- DRM_DEBUG_ATOMIC("triggering commit\n");
- kms->funcs->commit(kms, state);
- }
+ if (async) {
+ struct msm_pending_timer *timer =
+ &kms->pending_timers[drm_crtc_index(async_crtc)];
- if (!state->legacy_cursor_update)
- msm_atomic_wait_for_commit_done(dev, state);
+ /* async updates are limited to single-crtc updates: */
+ WARN_ON(crtc_mask != drm_crtc_mask(async_crtc));
- kms->funcs->complete_commit(kms, state);
+ /*
+ * Start timer if we don't already have an update pending
+ * on this crtc:
+ */
+ if (!(kms->pending_crtc_mask & crtc_mask)) {
+ ktime_t vsync_time, wakeup_time;
- drm_atomic_helper_commit_hw_done(state);
+ kms->pending_crtc_mask |= crtc_mask;
+
+ vsync_time = kms->funcs->vsync_time(kms, async_crtc);
+ wakeup_time = ktime_sub(vsync_time, ms_to_ktime(1));
+
+ hrtimer_start(&timer->timer, wakeup_time,
+ HRTIMER_MODE_ABS);
+ }
+ kms->funcs->disable_commit(kms);
+ mutex_unlock(&kms->commit_lock);
+
+ /*
+ * At this point, from drm core's perspective, we
+ * are done with the atomic update, so we can just
+ * go ahead and signal that it is done:
+ */
+ drm_atomic_helper_commit_hw_done(state);
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+ trace_msm_atomic_commit_tail_finish(async, crtc_mask);
+
+ return;
+ }
+
+ /*
+ * If there is any async flush pending on updated crtcs, fold
+ * them into the current flush.
+ */
+ kms->pending_crtc_mask &= ~crtc_mask;
+
+ /*
+ * Flush hardware updates:
+ */
+ trace_msm_atomic_flush_commit(crtc_mask);
+ kms->funcs->flush_commit(kms, crtc_mask);
+ mutex_unlock(&kms->commit_lock);
+
+ /*
+ * Wait for flush to complete:
+ */
+ trace_msm_atomic_wait_flush_start(crtc_mask);
+ kms->funcs->wait_flush(kms, crtc_mask);
+ trace_msm_atomic_wait_flush_finish(crtc_mask);
+
+ mutex_lock(&kms->commit_lock);
+ kms->funcs->complete_commit(kms, crtc_mask);
+ mutex_unlock(&kms->commit_lock);
+ kms->funcs->disable_commit(kms);
+
+ drm_atomic_helper_commit_hw_done(state);
drm_atomic_helper_cleanup_planes(dev, state);
+
+ trace_msm_atomic_commit_tail_finish(async, crtc_mask);
}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+#if !defined(_MSM_GPU_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _MSM_GPU_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM drm_msm_atomic
+#define TRACE_INCLUDE_FILE msm_atomic_trace
+
+TRACE_EVENT(msm_atomic_commit_tail_start,
+ TP_PROTO(bool async, unsigned crtc_mask),
+ TP_ARGS(async, crtc_mask),
+ TP_STRUCT__entry(
+ __field(bool, async)
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->async = async;
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("async=%d crtc_mask=%x",
+ __entry->async, __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_commit_tail_finish,
+ TP_PROTO(bool async, unsigned crtc_mask),
+ TP_ARGS(async, crtc_mask),
+ TP_STRUCT__entry(
+ __field(bool, async)
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->async = async;
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("async=%d crtc_mask=%x",
+ __entry->async, __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_async_commit_start,
+ TP_PROTO(unsigned crtc_mask),
+ TP_ARGS(crtc_mask),
+ TP_STRUCT__entry(
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("crtc_mask=%x",
+ __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_async_commit_finish,
+ TP_PROTO(unsigned crtc_mask),
+ TP_ARGS(crtc_mask),
+ TP_STRUCT__entry(
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("crtc_mask=%x",
+ __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_wait_flush_start,
+ TP_PROTO(unsigned crtc_mask),
+ TP_ARGS(crtc_mask),
+ TP_STRUCT__entry(
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("crtc_mask=%x",
+ __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_wait_flush_finish,
+ TP_PROTO(unsigned crtc_mask),
+ TP_ARGS(crtc_mask),
+ TP_STRUCT__entry(
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("crtc_mask=%x",
+ __entry->crtc_mask)
+);
+
+TRACE_EVENT(msm_atomic_flush_commit,
+ TP_PROTO(unsigned crtc_mask),
+ TP_ARGS(crtc_mask),
+ TP_STRUCT__entry(
+ __field(u32, crtc_mask)
+ ),
+ TP_fast_assign(
+ __entry->crtc_mask = crtc_mask;
+ ),
+ TP_printk("crtc_mask=%x",
+ __entry->crtc_mask)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/msm
+#include <trace/define_trace.h>
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include "msm_atomic_trace.h"
*/
#ifdef CONFIG_DEBUG_FS
+
#include <linux/debugfs.h>
+
+#include <drm/drm_debugfs.h>
+#include <drm/drm_file.h>
+
#include "msm_drv.h"
#include "msm_gpu.h"
#include "msm_kms.h"
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/dma-mapping.h>
#include <linux/kthread.h>
+#include <linux/uaccess.h>
#include <uapi/linux/sched/types.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_irq.h>
+#include <drm/drm_prime.h>
#include <drm/drm_of.h>
+#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "msm_debugfs.h"
#include "msm_kms.h"
#include "adreno/adreno_gpu.h"
-
/*
* MSM driver version:
* - 1.0.0 - initial interface
* Util/helpers:
*/
-int msm_clk_bulk_get(struct device *dev, struct clk_bulk_data **bulk)
-{
- struct property *prop;
- const char *name;
- struct clk_bulk_data *local;
- int i = 0, ret, count;
-
- count = of_property_count_strings(dev->of_node, "clock-names");
- if (count < 1)
- return 0;
-
- local = devm_kcalloc(dev, sizeof(struct clk_bulk_data *),
- count, GFP_KERNEL);
- if (!local)
- return -ENOMEM;
-
- of_property_for_each_string(dev->of_node, "clock-names", prop, name) {
- local[i].id = devm_kstrdup(dev, name, GFP_KERNEL);
- if (!local[i].id) {
- devm_kfree(dev, local);
- return -ENOMEM;
- }
-
- i++;
- }
-
- ret = devm_clk_bulk_get(dev, count, local);
-
- if (ret) {
- for (i = 0; i < count; i++)
- devm_kfree(dev, (void *) local[i].id);
- devm_kfree(dev, local);
-
- return ret;
- }
-
- *bulk = local;
- return count;
-}
-
struct clk *msm_clk_bulk_get_clock(struct clk_bulk_data *bulk, int count,
const char *name)
{
ddev->mode_config.normalize_zpos = true;
if (kms) {
+ kms->dev = ddev;
ret = kms->funcs->hw_init(kms);
if (ret) {
DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret);
#include <linux/sizes.h>
#include <linux/kthread.h>
-#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_plane_helper.h>
uint32_t pixel_format;
};
+struct msm_pending_timer;
+
int msm_atomic_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state);
+void msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
+ struct msm_kms *kms, int crtc_idx);
void msm_atomic_commit_tail(struct drm_atomic_state *state);
struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev);
void msm_atomic_state_clear(struct drm_atomic_state *state);
#endif
struct clk *msm_clk_get(struct platform_device *pdev, const char *name);
-int msm_clk_bulk_get(struct device *dev, struct clk_bulk_data **bulk);
struct clk *msm_clk_bulk_get_clock(struct clk_bulk_data *bulk, int count,
const char *name);
#include <drm/drm_crtc.h>
#include <drm/drm_damage_helper.h>
+#include <drm/drm_file.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
#include "msm_drv.h"
#include "msm_kms.h"
if (ret)
goto fini;
+ /* the fw fb could be anywhere in memory */
+ drm_fb_helper_remove_conflicting_framebuffers(NULL, "msm", false);
+
ret = drm_fb_helper_initial_config(helper, 32);
if (ret)
goto fini;
#include <linux/dma-buf.h>
#include <linux/pfn_t.h>
+#include <drm/drm_prime.h>
+
#include "msm_drv.h"
#include "msm_fence.h"
#include "msm_gem.h"
{
struct device *dev = msm_obj->base.dev->dev;
- if (get_dma_ops(dev)) {
+ if (get_dma_ops(dev) && IS_ENABLED(CONFIG_ARM64)) {
dma_sync_sg_for_device(dev, msm_obj->sgt->sgl,
msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
} else {
{
struct device *dev = msm_obj->base.dev->dev;
- if (get_dma_ops(dev)) {
+ if (get_dma_ops(dev) && IS_ENABLED(CONFIG_ARM64)) {
dma_sync_sg_for_cpu(dev, msm_obj->sgt->sgl,
msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
} else {
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/dma-buf.h>
+
+#include <drm/drm_prime.h>
+
#include "msm_drv.h"
#include "msm_gem.h"
-#include <linux/dma-buf.h>
-
struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <linux/file.h>
#include <linux/sync_file.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_file.h>
#include "msm_drv.h"
#include "msm_gpu.h"
uint32_t nr_cmds)
{
struct msm_gem_submit *submit;
- uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) +
- ((u64)nr_cmds * sizeof(submit->cmd[0]));
+ uint64_t sz = struct_size(submit, bos, nr_bos) +
+ ((u64)nr_cmds * sizeof(submit->cmd[0]));
if (sz > SIZE_MAX)
return NULL;
static int get_clocks(struct platform_device *pdev, struct msm_gpu *gpu)
{
- int ret = msm_clk_bulk_get(&pdev->dev, &gpu->grp_clks);
+ int ret = devm_clk_bulk_get_all(&pdev->dev, &gpu->grp_clks);
if (ret < 1) {
gpu->nr_clocks = 0;
#include <linux/tracepoint.h>
#undef TRACE_SYSTEM
-#define TRACE_SYSTEM drm_msm
+#define TRACE_SYSTEM drm_msm_gpu
#define TRACE_INCLUDE_FILE msm_gpu_trace
TRACE_EVENT(msm_gpu_submit,
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 The Linux Foundation. All rights reserved. */
+#include <linux/dma-mapping.h>
+
#include "msm_drv.h"
#include "msm_mmu.h"
#include "adreno/adreno_gpu.h"
irqreturn_t (*irq)(struct msm_kms *kms);
int (*enable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
void (*disable_vblank)(struct msm_kms *kms, struct drm_crtc *crtc);
- /* modeset, bracketing atomic_commit(): */
+
+ /*
+ * Atomic commit handling:
+ *
+ * Note that in the case of async commits, the funcs which take
+ * a crtc_mask (ie. ->flush_commit(), and ->complete_commit())
+ * might not be evenly balanced with ->prepare_commit(), however
+ * each crtc that effected by a ->prepare_commit() (potentially
+ * multiple times) will eventually (at end of vsync period) be
+ * flushed and completed.
+ *
+ * This has some implications about tracking of cleanup state,
+ * for example SMP blocks to release after commit completes. Ie.
+ * cleanup state should be also duplicated in the various
+ * duplicate_state() methods, as the current cleanup state at
+ * ->complete_commit() time may have accumulated cleanup work
+ * from multiple commits.
+ */
+
+ /**
+ * Enable/disable power/clks needed for hw access done in other
+ * commit related methods.
+ *
+ * If mdp4 is migrated to runpm, we could probably drop these
+ * and use runpm directly.
+ */
+ void (*enable_commit)(struct msm_kms *kms);
+ void (*disable_commit)(struct msm_kms *kms);
+
+ /**
+ * If the kms backend supports async commit, it should implement
+ * this method to return the time of the next vsync. This is
+ * used to determine a time slightly before vsync, for the async
+ * commit timer to run and complete an async commit.
+ */
+ ktime_t (*vsync_time)(struct msm_kms *kms, struct drm_crtc *crtc);
+
+ /**
+ * Prepare for atomic commit. This is called after any previous
+ * (async or otherwise) commit has completed.
+ */
void (*prepare_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
- void (*commit)(struct msm_kms *kms, struct drm_atomic_state *state);
- void (*complete_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
- /* functions to wait for atomic commit completed on each CRTC */
- void (*wait_for_crtc_commit_done)(struct msm_kms *kms,
- struct drm_crtc *crtc);
+
+ /**
+ * Flush an atomic commit. This is called after the hardware
+ * updates have already been pushed down to effected planes/
+ * crtcs/encoders/connectors.
+ */
+ void (*flush_commit)(struct msm_kms *kms, unsigned crtc_mask);
+
+ /**
+ * Wait for any in-progress flush to complete on the specified
+ * crtcs. This should not block if there is no in-progress
+ * commit (ie. don't just wait for a vblank), as it will also
+ * be called before ->prepare_commit() to ensure any potential
+ * "async" commit has completed.
+ */
+ void (*wait_flush)(struct msm_kms *kms, unsigned crtc_mask);
+
+ /**
+ * Clean up after commit is completed. This is called after
+ * ->wait_flush(), to give the backend a chance to do any
+ * post-commit cleanup.
+ */
+ void (*complete_commit)(struct msm_kms *kms, unsigned crtc_mask);
+
+ /*
+ * Format handling:
+ */
+
/* get msm_format w/ optional format modifiers from drm_mode_fb_cmd2 */
const struct msm_format *(*get_format)(struct msm_kms *kms,
const uint32_t format,
const struct msm_format *msm_fmt,
const struct drm_mode_fb_cmd2 *cmd,
struct drm_gem_object **bos);
+
/* misc: */
long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
struct drm_encoder *encoder);
#endif
};
+struct msm_kms;
+
+/*
+ * A per-crtc timer for pending async atomic flushes. Scheduled to expire
+ * shortly before vblank to flush pending async updates.
+ */
+struct msm_pending_timer {
+ struct hrtimer timer;
+ struct work_struct work;
+ struct msm_kms *kms;
+ unsigned crtc_idx;
+};
+
struct msm_kms {
const struct msm_kms_funcs *funcs;
+ struct drm_device *dev;
/* irq number to be passed on to drm_irq_install */
int irq;
/* mapper-id used to request GEM buffer mapped for scanout: */
struct msm_gem_address_space *aspace;
+
+ /*
+ * For async commit, where ->flush_commit() and later happens
+ * from the crtc's pending_timer close to end of the frame:
+ */
+ struct mutex commit_lock;
+ unsigned pending_crtc_mask;
+ struct msm_pending_timer pending_timers[MAX_CRTCS];
};
static inline void msm_kms_init(struct msm_kms *kms,
const struct msm_kms_funcs *funcs)
{
+ unsigned i;
+
+ mutex_init(&kms->commit_lock);
kms->funcs = funcs;
+
+ for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++)
+ msm_atomic_init_pending_timer(&kms->pending_timers[i], kms, i);
}
struct msm_kms *mdp4_kms_init(struct drm_device *dev);
int mdp5_mdss_init(struct drm_device *dev);
int dpu_mdss_init(struct drm_device *dev);
+#define for_each_crtc_mask(dev, crtc, crtc_mask) \
+ drm_for_each_crtc(crtc, dev) \
+ for_each_if (drm_crtc_mask(crtc) & (crtc_mask))
+
#endif /* __MSM_KMS_H__ */
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_file.h>
#include "msm_drv.h"
#include "msm_gpu.h"
#ifdef CONFIG_DEBUG_FS
-#include <linux/kfifo.h>
-#include <linux/debugfs.h>
#include <linux/circ_buf.h>
+#include <linux/debugfs.h>
+#include <linux/kfifo.h>
+#include <linux/uaccess.h>
#include <linux/wait.h>
+#include <drm/drm_file.h>
+
#include "msm_drv.h"
#include "msm_gpu.h"
#include "msm_gem.h"
*/
#include <linux/kref.h>
+#include <linux/uaccess.h>
+
#include "msm_gpu.h"
void msm_submitqueue_destroy(struct kref *kref)