Commit 0173ecf2 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-msm-next-2019-09-06' of https://gitlab.freedesktop.org/drm/msm into drm-next

+ move msm8998 (snapdragon 835) display support
+ dpu fixes/cleanup
+ better async commit support for cursor updates
   (for dpu for now, I'll add mdp5 and possibly
   mdp4 once the movers deliver boxes full of my
   older hardware, so for v5.5)
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Rob Clark <robdclark@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGuKVayu9bCuVe1RhzS6N6sHTrv4SVAh=qyCrmubX24Xag@mail.gmail.com
parents 9a60b299 9f614197
......@@ -14,11 +14,11 @@ config DRM_MSM
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.
......
......@@ -75,6 +75,7 @@ msm-y := \
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 \
......
......@@ -2,9 +2,11 @@
/* 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"
......
......@@ -1172,7 +1172,7 @@ static int a6xx_gmu_pwrlevels_probe(struct a6xx_gmu *gmu)
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;
......
......@@ -181,6 +181,7 @@ MODULE_FIRMWARE("qcom/a530_zap.b01");
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)
{
......
......@@ -214,7 +214,6 @@ static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms,
*/
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;
......@@ -233,22 +232,9 @@ void dpu_core_perf_crtc_release_bw(struct drm_crtc *crtc)
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);
......
......@@ -9,11 +9,13 @@
#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"
......@@ -292,19 +294,6 @@ void dpu_crtc_vblank_callback(struct drm_crtc *crtc)
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,
......@@ -324,17 +313,12 @@ static void dpu_crtc_frame_event_work(struct kthread_work *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);
......@@ -407,13 +391,8 @@ static void dpu_crtc_frame_event_cb(void *data, u32 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));
}
......@@ -623,13 +602,12 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *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(),
......@@ -647,37 +625,22 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc, bool async)
*/
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");
}
......@@ -729,6 +692,7 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
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");
......@@ -745,8 +709,15 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
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))
......@@ -760,7 +731,8 @@ static void dpu_crtc_disable(struct drm_crtc *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);
}
......@@ -792,6 +764,7 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
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");
......@@ -804,9 +777,19 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
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;
......@@ -981,6 +964,8 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
}
}
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",
......
......@@ -238,17 +238,14 @@ void dpu_crtc_vblank_callback(struct drm_crtc *crtc);
/**
* 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
......
......@@ -6,14 +6,16 @@
*/
#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"
......@@ -1421,19 +1423,12 @@ static void dpu_encoder_off_work(struct work_struct *work)
* 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;
......@@ -1445,10 +1440,7 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc,
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);
......@@ -1559,18 +1551,12 @@ static void dpu_encoder_helper_hw_reset(struct dpu_encoder_phys *phys_enc)
* 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 */
......@@ -1592,13 +1578,12 @@ static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc,
* 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);
}
......@@ -1608,7 +1593,7 @@ static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc,
_dpu_encoder_trigger_flush(
&dpu_enc->base,
dpu_enc->cur_master,
pending_flush, async);
pending_flush);
}
_dpu_encoder_trigger_start(dpu_enc->cur_master);
......@@ -1695,8 +1680,7 @@ static u32 _dpu_encoder_calculate_linetime(struct dpu_encoder_virt *dpu_enc,
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;
......@@ -1783,7 +1767,7 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
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);
......@@ -1791,17 +1775,13 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
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));
......@@ -1830,39 +1810,28 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, bool async)
}
}
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++) {
......@@ -1872,7 +1841,7 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc, bool async)
}
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,
......
......@@ -68,9 +68,8 @@ void dpu_encoder_register_frame_event_callback(struct drm_encoder *encoder,
* 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
......@@ -83,9 +82,13 @@ void dpu_encoder_trigger_kickoff_pending(struct drm_encoder *encoder);
* 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
......
......@@ -720,9 +720,6 @@ static int dpu_encoder_phys_cmd_wait_for_vblank(
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
......
......@@ -324,6 +324,10 @@ static void dpu_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
/* 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");
}
......@@ -483,8 +487,8 @@ static void dpu_encoder_phys_vid_get_hw_resources(
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;
......@@ -499,10 +503,6 @@ static int _dpu_encoder_phys_vid_wait_for_vblank(
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;
}
......@@ -512,18 +512,29 @@ static int _dpu_encoder_phys_vid_wait_for_vblank(
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(
......@@ -595,7 +606,7 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
* 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",
......@@ -612,11 +623,6 @@ static void dpu_encoder_phys_vid_handle_post_kickoff(
{
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
......@@ -681,7 +687,7 @@ static void dpu_encoder_phys_vid_init_ops(struct dpu_encoder_phys_ops *ops)
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;
......
......@@ -9,7 +9,6 @@
#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
......
......@@ -102,9 +102,6 @@ static inline void dpu_hw_ctl_update_pending_flush(struct dpu_hw_ctl *ctx,
static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx)
{
if (!ctx)
return 0x0;
return ctx->pending_flush_mask;
}
......
......@@ -7,6 +7,7 @@
#include <linux/clk/clk-conf.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <drm/drm_print.h>
......
......@@ -5,7 +5,6 @@
#ifndef __DPU_IO_UTIL_H__
#define __DPU_IO_UTIL_H__
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/types.h>
......@@ -14,12 +13,6 @@
#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,
......@@ -34,8 +27,6 @@ struct dss_clk {
};
struct dss_module_power {
unsigned int num_gpio;
struct dss_gpio *gpio_config;
unsigned int num_clk;
struct dss_clk *clk_config;
};
......
......@@ -7,10 +7,12 @@
#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"
......@@ -248,6 +250,32 @@ static void dpu_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
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)
{
......@@ -267,7 +295,6 @@ static void dpu_kms_prepare_commit(struct msm_kms *kms,
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) {
......@@ -278,6 +305,20 @@ static void dpu_kms_prepare_commit(struct msm_kms *kms,
}
}
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.
......@@ -298,52 +339,18 @@ void dpu_kms_encoder_enable(struct drm_encoder *encoder)
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");
}
......@@ -389,6 +396,15 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
}
}
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)
......@@ -490,11 +506,6 @@ static int _dpu_kms_drm_obj_init(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;
......@@ -686,10 +697,13 @@ static const struct msm_kms_funcs kms_funcs = {
.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,
......@@ -800,6 +814,8 @@ static int dpu_kms_hw_init(struct msm_kms *kms)
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);
......
......@@ -8,6 +8,8 @@
#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"
......@@ -120,6 +122,14 @@ struct dpu_kms {
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 {
......
......@@ -3,6 +3,10 @@
* 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>
......@@ -22,7 +26,6 @@ struct dpu_mdss {
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];
......@@ -287,10 +290,6 @@ int dpu_mdss_init(struct drm_device *dev)
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:
......
......@@ -10,8 +10,9 @@
#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"
......@@ -1036,8 +1037,21 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
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);
......@@ -1518,6 +1532,13 @@ struct drm_plane *dpu_plane_init(struct drm_device *dev,
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 */
......
......@@ -392,7 +392,7 @@ TRACE_EVENT(dpu_enc_rc,
__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)
......
......@@ -5,6 +5,7 @@
#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"
......@@ -264,11 +265,6 @@ void dpu_vbif_clear_errors(struct dpu_kms *dpu_kms)
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) {
......@@ -286,11 +282,6 @@ void dpu_vbif_init_memtypes(struct dpu_kms *dpu_kms)
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) {
......
......@@ -8,6 +8,7 @@
#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"
......
......@@ -5,6 +5,7 @@
*/
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "mdp4_kms.h"
......
......@@ -4,6 +4,9 @@
* Author: Rob Clark <robdclark@gmail.com>
*/
#include <linux/delay.h>
#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "msm_gem.h"
......@@ -93,40 +96,51 @@ static int mdp4_hw_init(struct msm_kms *kms)
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,
......@@ -178,9 +192,12 @@ static const struct mdp_kms_funcs kms_funcs = {
.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,
......
......@@ -5,6 +5,8 @@
* Author: Vinay Simha <vinaysimha@inforcecomputing.com>
*/
#include <linux/delay.h>
#include <drm/drm_crtc.h>
#include <drm/drm_probe_helper.h>
......
......@@ -5,8 +5,6 @@
* Author: Vinay Simha <vinaysimha@inforcecomputing.com>
*/
#include <linux/gpio.h>
#include "mdp4_kms.h"
struct mdp4_lvds_connector {
......
......@@ -5,6 +5,8 @@
*/
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
#include "mdp4_kms.h"
#define DOWN_SCALE_MAX 8
......
......@@ -630,7 +630,115 @@ const struct mdp5_cfg_hw msm8917_config = {
.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 } },
......@@ -640,6 +748,10 @@ static const struct mdp5_cfg_handler cfg_handlers[] = {
{ .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)
......@@ -668,8 +780,9 @@ struct mdp5_cfg_handler *mdp5_cfg_init(struct mdp5_kms *mdp5_kms,
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)) {
......@@ -677,15 +790,24 @@ struct mdp5_cfg_handler *mdp5_cfg_init(struct mdp5_kms *mdp5_kms,
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;
......
......@@ -6,10 +6,13 @@
*/
#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"
......
......@@ -253,7 +253,7 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
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;
......@@ -695,7 +695,7 @@ struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev,
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;
......
......@@ -7,6 +7,7 @@
#include <linux/irq.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "msm_drv.h"
#include "mdp5_kms.h"
......
......@@ -5,9 +5,15 @@
* 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"
......@@ -140,40 +146,52 @@ static int mdp5_global_obj_init(struct mdp5_kms *mdp5_kms)
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,
......@@ -271,9 +289,12 @@ static const struct mdp_kms_funcs kms_funcs = {
.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,
......@@ -663,6 +684,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
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;
......@@ -702,7 +724,11 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
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);
......
......@@ -6,7 +6,9 @@
*/
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>
#include "mdp5_kms.h"
struct mdp5_plane {
......
......@@ -5,6 +5,7 @@
* Author: Rob Clark <robdclark@gmail.com>
*/
#include <drm/drm_fourcc.h>
#include <drm/drm_util.h>
#include "mdp5_kms.h"
......
......@@ -174,7 +174,7 @@ const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format,
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];
......
......@@ -5,19 +5,19 @@
#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"
......@@ -421,15 +421,15 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
}
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;
}
......
......@@ -396,8 +396,12 @@ static int dsi_phy_regulator_init(struct msm_dsi_phy *phy)
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;
}
......@@ -584,10 +588,8 @@ static int dsi_phy_driver_probe(struct platform_device *pdev)
}
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)) {
......
......@@ -3,6 +3,8 @@
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
......
......@@ -3,6 +3,8 @@
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
......
......@@ -6,8 +6,8 @@
#ifndef __DSI_PLL_H__
#define __DSI_PLL_H__
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "dsi.h"
......
......@@ -425,38 +425,6 @@ static const struct {
{ "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
*/
......@@ -582,11 +550,39 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
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;
......
......@@ -11,6 +11,7 @@
#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"
......@@ -22,10 +23,9 @@ struct hdmi_phy;
struct hdmi_platform_config;
struct hdmi_gpio_data {
int num;
struct gpio_desc *gpiod;
bool output;
int value;
const char *label;
};
struct hdmi_audio {
......
......@@ -4,6 +4,8 @@
* Author: Rob Clark <robdclark@gmail.com>
*/
#include <linux/delay.h>
#include "hdmi.h"
struct hdmi_bridge {
......
......@@ -4,7 +4,8 @@
* 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"
......@@ -68,30 +69,21 @@ static void msm_hdmi_phy_reset(struct hdmi *hdmi)
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);
}
}
}
......@@ -101,29 +93,20 @@ static int gpio_config(struct hdmi *hdmi, bool on)
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)
......@@ -311,7 +294,7 @@ static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
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;
}
......@@ -330,7 +313,7 @@ static enum drm_connector_status hdmi_connector_detect(
* 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 {
......
......@@ -4,6 +4,7 @@
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "hdmi.h"
......
......@@ -4,6 +4,8 @@
* Author: Rob Clark <robdclark@gmail.com>
*/
#include <linux/delay.h>
#include "hdmi.h"
static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy,
......
......@@ -6,6 +6,8 @@
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "hdmi.h"
struct hdmi_pll_8960 {
......
......@@ -6,45 +6,137 @@
#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)
......@@ -52,26 +144,104 @@ 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);
}
/* 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>
// SPDX-License-Identifier: GPL-2.0
#define CREATE_TRACE_POINTS
#include "msm_atomic_trace.h"
......@@ -5,7 +5,12 @@
*/
#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"
......
......@@ -5,9 +5,18 @@
* 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"
......@@ -17,7 +26,6 @@
#include "msm_kms.h"
#include "adreno/adreno_gpu.h"
/*
* MSM driver version:
* - 1.0.0 - initial interface
......@@ -75,46 +83,6 @@ module_param(modeset, bool, 0600);
* 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)
{
......@@ -505,6 +473,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
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);
......
......@@ -25,7 +25,6 @@
#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>
......@@ -222,8 +221,12 @@ struct msm_format {
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);
......@@ -399,7 +402,6 @@ static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {}
#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);
......
......@@ -6,6 +6,8 @@
#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>
......
......@@ -6,6 +6,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include "msm_drv.h"
#include "msm_kms.h"
......@@ -169,6 +170,9 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
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;
......
......@@ -9,6 +9,8 @@
#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"
......@@ -50,7 +52,7 @@ static void sync_for_device(struct msm_gem_object *msm_obj)
{
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 {
......@@ -63,7 +65,7 @@ static void sync_for_cpu(struct msm_gem_object *msm_obj)
{
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 {
......
......@@ -4,11 +4,13 @@
* 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);
......
......@@ -4,7 +4,11 @@
* 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"
......@@ -26,8 +30,8 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev,
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;
......
......@@ -784,7 +784,7 @@ static irqreturn_t irq_handler(int irq, void *data)
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;
......
......@@ -5,7 +5,7 @@
#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"
......
......@@ -30,13 +30,76 @@ struct msm_kms_funcs {
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,
......@@ -46,6 +109,7 @@ struct msm_kms_funcs {
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);
......@@ -64,20 +128,48 @@ struct msm_kms_funcs {
#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);
......@@ -98,4 +190,8 @@ struct msm_mdss {
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__ */
......@@ -15,6 +15,9 @@
#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"
......
......@@ -31,11 +31,14 @@
#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"
......
......@@ -3,6 +3,8 @@
*/
#include <linux/kref.h>
#include <linux/uaccess.h>
#include "msm_gpu.h"
void msm_submitqueue_destroy(struct kref *kref)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment