forked from Minki/linux
577e2a9dfc
As msm_drm_uninit() is called from the msm_drm_init() error path,
additional care should be necessary as not to call the free_irq() for
the IRQ that was not requested before (because an error occured earlier
than the request_irq() call).
This fixed the issue reported with the following backtrace:
[ 8.571329] Trying to free already-free IRQ 187
[ 8.571339] WARNING: CPU: 0 PID: 76 at kernel/irq/manage.c:1895 free_irq+0x1e0/0x35c
[ 8.588746] Modules linked in: pmic_glink pdr_interface fastrpc qrtr_smd snd_soc_hdmi_codec msm fsa4480 gpu_sched drm_dp_aux_bus qrtr i2c_qcom_geni crct10dif_ce qcom_stats qcom_q6v5_pas drm_display_helper gpi qcom_pil_info drm_kms_helper qcom_q6v5 qcom_sysmon qcom_common qcom_glink_smem qcom_rng mdt_loader qmi_helpers phy_qcom_qmp ufs_qcom typec qnoc_sm8350 socinfo rmtfs_mem fuse drm ipv6
[ 8.624154] CPU: 0 PID: 76 Comm: kworker/u16:2 Not tainted 5.18.0-rc5-next-20220506-00033-g6cee8cab6089-dirty #419
[ 8.624161] Hardware name: Qualcomm Technologies, Inc. SM8350 HDK (DT)
[ 8.641496] Workqueue: events_unbound deferred_probe_work_func
[ 8.647510] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 8.654681] pc : free_irq+0x1e0/0x35c
[ 8.658454] lr : free_irq+0x1e0/0x35c
[ 8.662228] sp : ffff800008ab3950
[ 8.665642] x29: ffff800008ab3950 x28: 0000000000000000 x27: ffff16350f56a700
[ 8.672994] x26: ffff1635025df080 x25: ffff16350251badc x24: ffff16350251bb90
[ 8.680343] x23: 0000000000000000 x22: 00000000000000bb x21: ffff16350e8f9800
[ 8.687690] x20: ffff16350251ba00 x19: ffff16350cbd5880 x18: ffffffffffffffff
[ 8.695039] x17: 0000000000000000 x16: ffffa2dd12179434 x15: ffffa2dd1431d02d
[ 8.702391] x14: 0000000000000000 x13: ffffa2dd1431d028 x12: 662d79646165726c
[ 8.709740] x11: ffffa2dd13fd2438 x10: 000000000000000a x9 : 00000000000000bb
[ 8.717111] x8 : ffffa2dd13fd23f0 x7 : ffff800008ab3750 x6 : 00000000fffff202
[ 8.724487] x5 : ffff16377e870a18 x4 : 00000000fffff202 x3 : ffff735a6ae1b000
[ 8.731851] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff1635015f8000
[ 8.739217] Call trace:
[ 8.741755] free_irq+0x1e0/0x35c
[ 8.745198] msm_drm_uninit.isra.0+0x14c/0x294 [msm]
[ 8.750548] msm_drm_bind+0x28c/0x5d0 [msm]
[ 8.755081] try_to_bring_up_aggregate_device+0x164/0x1d0
[ 8.760657] __component_add+0xa0/0x170
[ 8.764626] component_add+0x14/0x20
[ 8.768337] dp_display_probe+0x2a4/0x464 [msm]
[ 8.773242] platform_probe+0x68/0xe0
[ 8.777043] really_probe.part.0+0x9c/0x28c
[ 8.781368] __driver_probe_device+0x98/0x144
[ 8.785871] driver_probe_device+0x40/0x140
[ 8.790191] __device_attach_driver+0xb4/0x120
[ 8.794788] bus_for_each_drv+0x78/0xd0
[ 8.798751] __device_attach+0xdc/0x184
[ 8.802713] device_initial_probe+0x14/0x20
[ 8.807031] bus_probe_device+0x9c/0xa4
[ 8.810991] deferred_probe_work_func+0x88/0xc0
[ 8.815667] process_one_work+0x1d0/0x320
[ 8.819809] worker_thread+0x14c/0x444
[ 8.823688] kthread+0x10c/0x110
[ 8.827036] ret_from_fork+0x10/0x20
Reported-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Fixes: f026e431cf
("drm/msm: Convert to Linux IRQ interfaces")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Patchwork: https://patchwork.freedesktop.org/patch/485422/
Link: https://lore.kernel.org/r/20220507010021.1667700-1-dmitry.baryshkov@linaro.org
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
207 lines
6.1 KiB
C
207 lines
6.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
|
|
* Copyright (C) 2013 Red Hat
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
*/
|
|
|
|
#ifndef __MSM_KMS_H__
|
|
#define __MSM_KMS_H__
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include "msm_drv.h"
|
|
|
|
#define MAX_PLANE 4
|
|
|
|
/* As there are different display controller blocks depending on the
|
|
* snapdragon version, the kms support is split out and the appropriate
|
|
* implementation is loaded at runtime. The kms module is responsible
|
|
* for constructing the appropriate planes/crtcs/encoders/connectors.
|
|
*/
|
|
struct msm_kms_funcs {
|
|
/* hw initialization: */
|
|
int (*hw_init)(struct msm_kms *kms);
|
|
/* irq handling: */
|
|
void (*irq_preinstall)(struct msm_kms *kms);
|
|
int (*irq_postinstall)(struct msm_kms *kms);
|
|
void (*irq_uninstall)(struct msm_kms *kms);
|
|
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);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/**
|
|
* 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 uint64_t modifiers);
|
|
/* do format checking on format modified through fb_cmd2 modifiers */
|
|
int (*check_modified_format)(const struct msm_kms *kms,
|
|
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);
|
|
int (*set_split_display)(struct msm_kms *kms,
|
|
struct drm_encoder *encoder,
|
|
struct drm_encoder *slave_encoder,
|
|
bool is_cmd_mode);
|
|
/* cleanup: */
|
|
void (*destroy)(struct msm_kms *kms);
|
|
|
|
/* snapshot: */
|
|
void (*snapshot)(struct msm_disp_state *disp_state, struct msm_kms *kms);
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
/* debugfs: */
|
|
int (*debugfs_init)(struct msm_kms *kms, struct drm_minor *minor);
|
|
#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 msm_hrtimer_work work;
|
|
struct kthread_worker *worker;
|
|
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 msm_irq_install */
|
|
int irq;
|
|
bool irq_requested;
|
|
|
|
/* mapper-id used to request GEM buffer mapped for scanout: */
|
|
struct msm_gem_address_space *aspace;
|
|
|
|
/* disp snapshot support */
|
|
struct kthread_worker *dump_worker;
|
|
struct kthread_work dump_work;
|
|
struct mutex dump_mutex;
|
|
|
|
/*
|
|
* 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[MAX_CRTCS];
|
|
unsigned pending_crtc_mask;
|
|
struct msm_pending_timer pending_timers[MAX_CRTCS];
|
|
};
|
|
|
|
static inline int msm_kms_init(struct msm_kms *kms,
|
|
const struct msm_kms_funcs *funcs)
|
|
{
|
|
unsigned i, ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(kms->commit_lock); i++)
|
|
mutex_init(&kms->commit_lock[i]);
|
|
|
|
kms->funcs = funcs;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++) {
|
|
ret = msm_atomic_init_pending_timer(&kms->pending_timers[i], kms, i);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void msm_kms_destroy(struct msm_kms *kms)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++)
|
|
msm_atomic_destroy_pending_timer(&kms->pending_timers[i]);
|
|
}
|
|
|
|
#define for_each_crtc_mask(dev, crtc, crtc_mask) \
|
|
drm_for_each_crtc(crtc, dev) \
|
|
for_each_if (drm_crtc_mask(crtc) & (crtc_mask))
|
|
|
|
#define for_each_crtc_mask_reverse(dev, crtc, crtc_mask) \
|
|
drm_for_each_crtc_reverse(crtc, dev) \
|
|
for_each_if (drm_crtc_mask(crtc) & (crtc_mask))
|
|
|
|
#endif /* __MSM_KMS_H__ */
|