Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
This is the 2nd pull request for the malidp-next. The new patches add additional support for Arm Mali D71 so that it can now be enabled correctly and brought up on any SoC that contains the IP. From now on we will start focusing on adding writeback, scaling and other useful features to bring the driver to the same level of maturity as mali-dp. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Liviu Dudau <Liviu.Dudau@arm.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190507103712.GJ15144@e110455-lin.cambridge.arm.com
This commit is contained in:
commit
f2e8d169b2
|
@ -20,4 +20,16 @@
|
|||
/* Mali-display product IDs */
|
||||
#define MALIDP_D71_PRODUCT_ID 0x0071
|
||||
|
||||
union komeda_config_id {
|
||||
struct {
|
||||
__u32 max_line_sz:16,
|
||||
n_pipelines:2,
|
||||
n_scalers:2, /* number of scalers per pipeline */
|
||||
n_layers:3, /* number of layers per pipeline */
|
||||
n_richs:3, /* number of rich layers per pipeline */
|
||||
reserved_bits:6;
|
||||
};
|
||||
__u32 value;
|
||||
};
|
||||
|
||||
#endif /* _MALIDP_PRODUCT_H_ */
|
||||
|
|
|
@ -9,6 +9,7 @@ komeda-y := \
|
|||
komeda_dev.o \
|
||||
komeda_format_caps.o \
|
||||
komeda_pipeline.o \
|
||||
komeda_pipeline_state.o \
|
||||
komeda_framebuffer.o \
|
||||
komeda_kms.o \
|
||||
komeda_crtc.o \
|
||||
|
|
|
@ -391,7 +391,7 @@ static void d71_compiz_dump(struct komeda_component *c, struct seq_file *sf)
|
|||
seq_printf(sf, "CU_USER_HIGH:\t\t0x%X\n", v[1]);
|
||||
}
|
||||
|
||||
struct komeda_component_funcs d71_compiz_funcs = {
|
||||
static struct komeda_component_funcs d71_compiz_funcs = {
|
||||
.update = d71_compiz_update,
|
||||
.disable = d71_component_disable,
|
||||
.dump_register = d71_compiz_dump,
|
||||
|
@ -467,7 +467,7 @@ static void d71_improc_dump(struct komeda_component *c, struct seq_file *sf)
|
|||
seq_printf(sf, "IPS_RGB_YUV_COEFF%u:\t0x%X\n", i, v[i]);
|
||||
}
|
||||
|
||||
struct komeda_component_funcs d71_improc_funcs = {
|
||||
static struct komeda_component_funcs d71_improc_funcs = {
|
||||
.update = d71_improc_update,
|
||||
.disable = d71_component_disable,
|
||||
.dump_register = d71_improc_dump,
|
||||
|
@ -543,7 +543,8 @@ static void d71_timing_ctrlr_update(struct komeda_component *c,
|
|||
malidp_write32(reg, BLK_CONTROL, value);
|
||||
}
|
||||
|
||||
void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf)
|
||||
static void d71_timing_ctrlr_dump(struct komeda_component *c,
|
||||
struct seq_file *sf)
|
||||
{
|
||||
u32 v[8], i;
|
||||
|
||||
|
@ -579,7 +580,7 @@ void d71_timing_ctrlr_dump(struct komeda_component *c, struct seq_file *sf)
|
|||
seq_printf(sf, "BS_USER:\t\t0x%X\n", v[4]);
|
||||
}
|
||||
|
||||
struct komeda_component_funcs d71_timing_ctrlr_funcs = {
|
||||
static struct komeda_component_funcs d71_timing_ctrlr_funcs = {
|
||||
.update = d71_timing_ctrlr_update,
|
||||
.disable = d71_timing_ctrlr_disable,
|
||||
.dump_register = d71_timing_ctrlr_dump,
|
||||
|
|
|
@ -243,6 +243,56 @@ static int d71_disable_irq(struct komeda_dev *mdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void d71_on_off_vblank(struct komeda_dev *mdev, int master_pipe, bool on)
|
||||
{
|
||||
struct d71_dev *d71 = mdev->chip_data;
|
||||
struct d71_pipeline *pipe = d71->pipes[master_pipe];
|
||||
|
||||
malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK,
|
||||
DOU_IRQ_PL0, on ? DOU_IRQ_PL0 : 0);
|
||||
}
|
||||
|
||||
static int to_d71_opmode(int core_mode)
|
||||
{
|
||||
switch (core_mode) {
|
||||
case KOMEDA_MODE_DISP0:
|
||||
return DO0_ACTIVE_MODE;
|
||||
case KOMEDA_MODE_DISP1:
|
||||
return DO1_ACTIVE_MODE;
|
||||
case KOMEDA_MODE_DUAL_DISP:
|
||||
return DO01_ACTIVE_MODE;
|
||||
case KOMEDA_MODE_INACTIVE:
|
||||
return INACTIVE_MODE;
|
||||
default:
|
||||
WARN(1, "Unknown operation mode");
|
||||
return INACTIVE_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
static int d71_change_opmode(struct komeda_dev *mdev, int new_mode)
|
||||
{
|
||||
struct d71_dev *d71 = mdev->chip_data;
|
||||
u32 opmode = to_d71_opmode(new_mode);
|
||||
int ret;
|
||||
|
||||
malidp_write32_mask(d71->gcu_addr, BLK_CONTROL, 0x7, opmode);
|
||||
|
||||
ret = dp_wait_cond(((malidp_read32(d71->gcu_addr, BLK_CONTROL) & 0x7) == opmode),
|
||||
100, 1000, 10000);
|
||||
|
||||
return ret > 0 ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void d71_flush(struct komeda_dev *mdev,
|
||||
int master_pipe, u32 active_pipes)
|
||||
{
|
||||
struct d71_dev *d71 = mdev->chip_data;
|
||||
u32 reg_offset = (master_pipe == 0) ?
|
||||
GCU_CONFIG_VALID0 : GCU_CONFIG_VALID1;
|
||||
|
||||
malidp_write32(d71->gcu_addr, reg_offset, GCU_CONFIG_CVAL);
|
||||
}
|
||||
|
||||
static int d71_reset(struct d71_dev *d71)
|
||||
{
|
||||
u32 __iomem *gcu = d71->gcu_addr;
|
||||
|
@ -459,6 +509,9 @@ static struct komeda_dev_funcs d71_chip_funcs = {
|
|||
.irq_handler = d71_irq_handler,
|
||||
.enable_irq = d71_enable_irq,
|
||||
.disable_irq = d71_disable_irq,
|
||||
.on_off_vblank = d71_on_off_vblank,
|
||||
.change_opmode = d71_change_opmode,
|
||||
.flush = d71_flush,
|
||||
};
|
||||
|
||||
struct komeda_dev_funcs *
|
||||
|
@ -467,6 +520,7 @@ d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip)
|
|||
chip->arch_id = malidp_read32(reg_base, GLB_ARCH_ID);
|
||||
chip->core_id = malidp_read32(reg_base, GLB_CORE_ID);
|
||||
chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO);
|
||||
chip->bus_width = D71_BUS_WIDTH_16_BYTES;
|
||||
|
||||
return &d71_chip_funcs;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,144 @@
|
|||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
|
||||
/**
|
||||
* komeda_crtc_atomic_check - build display output data flow
|
||||
* @crtc: DRM crtc
|
||||
* @state: the crtc state object
|
||||
*
|
||||
* crtc_atomic_check is the final check stage, so beside build a display data
|
||||
* pipeline according to the crtc_state, but still needs to release or disable
|
||||
* the unclaimed pipeline resources.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero for success or -errno
|
||||
*/
|
||||
static int
|
||||
komeda_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
|
||||
int err;
|
||||
|
||||
if (state->active) {
|
||||
err = komeda_build_display_data_flow(kcrtc, kcrtc_st);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* release unclaimed pipeline resources */
|
||||
err = komeda_release_unclaimed_resources(kcrtc->master, kcrtc_st);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
|
||||
{
|
||||
unsigned long mclk = kcrtc_st->base.adjusted_mode.clock * 1000;
|
||||
|
||||
return mclk;
|
||||
}
|
||||
|
||||
/* For active a crtc, mainly need two parts of preparation
|
||||
* 1. adjust display operation mode.
|
||||
* 2. enable needed clk
|
||||
*/
|
||||
static int
|
||||
komeda_crtc_prepare(struct komeda_crtc *kcrtc)
|
||||
{
|
||||
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state);
|
||||
unsigned long pxlclk_rate = kcrtc_st->base.adjusted_mode.clock * 1000;
|
||||
u32 new_mode;
|
||||
int err;
|
||||
|
||||
mutex_lock(&mdev->lock);
|
||||
|
||||
new_mode = mdev->dpmode | BIT(master->id);
|
||||
if (WARN_ON(new_mode == mdev->dpmode)) {
|
||||
err = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = mdev->funcs->change_opmode(mdev, new_mode);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
mdev->dpmode = new_mode;
|
||||
/* Only need to enable mclk on single display mode, but no need to
|
||||
* enable mclk it on dual display mode, since the dual mode always
|
||||
* switch from single display mode, the mclk already enabled, no need
|
||||
* to enable it again.
|
||||
*/
|
||||
if (new_mode != KOMEDA_MODE_DUAL_DISP) {
|
||||
err = clk_set_rate(mdev->mclk, komeda_calc_mclk(kcrtc_st));
|
||||
if (err)
|
||||
DRM_ERROR("failed to set mclk.\n");
|
||||
err = clk_prepare_enable(mdev->mclk);
|
||||
if (err)
|
||||
DRM_ERROR("failed to enable mclk.\n");
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(master->aclk);
|
||||
if (err)
|
||||
DRM_ERROR("failed to enable axi clk for pipe%d.\n", master->id);
|
||||
err = clk_set_rate(master->pxlclk, pxlclk_rate);
|
||||
if (err)
|
||||
DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id);
|
||||
err = clk_prepare_enable(master->pxlclk);
|
||||
if (err)
|
||||
DRM_ERROR("failed to enable pxl clk for pipe%d.\n", master->id);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&mdev->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
|
||||
{
|
||||
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
u32 new_mode;
|
||||
int err;
|
||||
|
||||
mutex_lock(&mdev->lock);
|
||||
|
||||
new_mode = mdev->dpmode & (~BIT(master->id));
|
||||
|
||||
if (WARN_ON(new_mode == mdev->dpmode)) {
|
||||
err = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = mdev->funcs->change_opmode(mdev, new_mode);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
mdev->dpmode = new_mode;
|
||||
|
||||
clk_disable_unprepare(master->pxlclk);
|
||||
clk_disable_unprepare(master->aclk);
|
||||
if (new_mode == KOMEDA_MODE_INACTIVE)
|
||||
clk_disable_unprepare(mdev->mclk);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&mdev->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
||||
struct komeda_events *evts)
|
||||
{
|
||||
|
@ -31,15 +169,264 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
|||
if (events & KOMEDA_EVENT_EOW)
|
||||
DRM_DEBUG("EOW.\n");
|
||||
|
||||
/* will handle it with crtc->flush */
|
||||
if (events & KOMEDA_EVENT_FLIP)
|
||||
DRM_DEBUG("FLIP Done.\n");
|
||||
if (events & KOMEDA_EVENT_FLIP) {
|
||||
unsigned long flags;
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
if (kcrtc->disable_done) {
|
||||
complete_all(kcrtc->disable_done);
|
||||
kcrtc->disable_done = NULL;
|
||||
} else if (crtc->state->event) {
|
||||
event = crtc->state->event;
|
||||
/*
|
||||
* Consume event before notifying drm core that flip
|
||||
* happened.
|
||||
*/
|
||||
crtc->state->event = NULL;
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
} else {
|
||||
DRM_WARN("CRTC[%d]: FLIP happen but no pending commit.\n",
|
||||
drm_crtc_index(&kcrtc->base));
|
||||
}
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
|
||||
static void
|
||||
komeda_crtc_do_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old)
|
||||
{
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc->state);
|
||||
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
|
||||
DRM_DEBUG_ATOMIC("CRTC%d_FLUSH: active_pipes: 0x%x, affected: 0x%x.\n",
|
||||
drm_crtc_index(crtc),
|
||||
kcrtc_st->active_pipes, kcrtc_st->affected_pipes);
|
||||
|
||||
/* step 1: update the pipeline/component state to HW */
|
||||
if (has_bit(master->id, kcrtc_st->affected_pipes))
|
||||
komeda_pipeline_update(master, old->state);
|
||||
|
||||
/* step 2: notify the HW to kickoff the update */
|
||||
mdev->funcs->flush(mdev, master->id, kcrtc_st->active_pipes);
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old)
|
||||
{
|
||||
komeda_crtc_prepare(to_kcrtc(crtc));
|
||||
drm_crtc_vblank_on(crtc);
|
||||
komeda_crtc_do_flush(crtc, old);
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old)
|
||||
{
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_crtc_state *old_st = to_kcrtc_st(old);
|
||||
struct komeda_dev *mdev = crtc->dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct completion *disable_done = &crtc->state->commit->flip_done;
|
||||
struct completion temp;
|
||||
int timeout;
|
||||
|
||||
DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x.\n",
|
||||
drm_crtc_index(crtc),
|
||||
old_st->active_pipes, old_st->affected_pipes);
|
||||
|
||||
if (has_bit(master->id, old_st->active_pipes))
|
||||
komeda_pipeline_disable(master, old->state);
|
||||
|
||||
/* crtc_disable has two scenarios according to the state->active switch.
|
||||
* 1. active -> inactive
|
||||
* this commit is a disable commit. and the commit will be finished
|
||||
* or done after the disable operation. on this case we can directly
|
||||
* use the crtc->state->event to tracking the HW disable operation.
|
||||
* 2. active -> active
|
||||
* the crtc->commit is not for disable, but a modeset operation when
|
||||
* crtc is active, such commit actually has been completed by 3
|
||||
* DRM operations:
|
||||
* crtc_disable, update_planes(crtc_flush), crtc_enable
|
||||
* so on this case the crtc->commit is for the whole process.
|
||||
* we can not use it for tracing the disable, we need a temporary
|
||||
* flip_done for tracing the disable. and crtc->state->event for
|
||||
* the crtc_enable operation.
|
||||
* That's also the reason why skip modeset commit in
|
||||
* komeda_crtc_atomic_flush()
|
||||
*/
|
||||
if (crtc->state->active) {
|
||||
struct komeda_pipeline_state *pipe_st;
|
||||
/* clear the old active_comps to zero */
|
||||
pipe_st = komeda_pipeline_get_old_state(master, old->state);
|
||||
pipe_st->active_comps = 0;
|
||||
|
||||
init_completion(&temp);
|
||||
kcrtc->disable_done = &temp;
|
||||
disable_done = &temp;
|
||||
}
|
||||
|
||||
mdev->funcs->flush(mdev, master->id, 0);
|
||||
|
||||
/* wait the disable take affect.*/
|
||||
timeout = wait_for_completion_timeout(disable_done, HZ);
|
||||
if (timeout == 0) {
|
||||
DRM_ERROR("disable pipeline%d timeout.\n", kcrtc->master->id);
|
||||
if (crtc->state->active) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
kcrtc->disable_done = NULL;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
drm_crtc_vblank_off(crtc);
|
||||
komeda_crtc_unprepare(kcrtc);
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old)
|
||||
{
|
||||
/* commit with modeset will be handled in enable/disable */
|
||||
if (drm_atomic_crtc_needs_modeset(crtc->state))
|
||||
return;
|
||||
|
||||
komeda_crtc_do_flush(crtc, old);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m)
|
||||
{
|
||||
struct komeda_dev *mdev = crtc->dev->dev_private;
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
long mode_clk, pxlclk;
|
||||
|
||||
if (m->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
/* main clock/AXI clk must be faster than pxlclk*/
|
||||
mode_clk = m->clock * 1000;
|
||||
pxlclk = clk_round_rate(master->pxlclk, mode_clk);
|
||||
if (pxlclk != mode_clk) {
|
||||
DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk);
|
||||
|
||||
return MODE_NOCLOCK;
|
||||
}
|
||||
|
||||
if (clk_round_rate(mdev->mclk, mode_clk) < pxlclk) {
|
||||
DRM_DEBUG_ATOMIC("mclk can't satisfy the requirement of %s-clk: %ld.\n",
|
||||
m->name, pxlclk);
|
||||
|
||||
return MODE_CLOCK_HIGH;
|
||||
}
|
||||
|
||||
if (clk_round_rate(master->aclk, mode_clk) < pxlclk) {
|
||||
DRM_DEBUG_ATOMIC("aclk can't satisfy the requirement of %s-clk: %ld.\n",
|
||||
m->name, pxlclk);
|
||||
|
||||
return MODE_CLOCK_HIGH;
|
||||
}
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *m,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
long mode_clk = m->clock * 1000;
|
||||
|
||||
adjusted_mode->clock = clk_round_rate(master->pxlclk, mode_clk) / 1000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
|
||||
.atomic_check = komeda_crtc_atomic_check,
|
||||
.atomic_flush = komeda_crtc_atomic_flush,
|
||||
.atomic_enable = komeda_crtc_atomic_enable,
|
||||
.atomic_disable = komeda_crtc_atomic_disable,
|
||||
.mode_valid = komeda_crtc_mode_valid,
|
||||
.mode_fixup = komeda_crtc_mode_fixup,
|
||||
};
|
||||
|
||||
static void komeda_crtc_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_crtc_state *state;
|
||||
|
||||
if (crtc->state)
|
||||
__drm_atomic_helper_crtc_destroy_state(crtc->state);
|
||||
|
||||
kfree(to_kcrtc_st(crtc->state));
|
||||
crtc->state = NULL;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state) {
|
||||
crtc->state = &state->base;
|
||||
crtc->state->crtc = crtc;
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_crtc_state *
|
||||
komeda_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_crtc_state *old = to_kcrtc_st(crtc->state);
|
||||
struct komeda_crtc_state *new;
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);
|
||||
|
||||
new->affected_pipes = old->active_pipes;
|
||||
|
||||
return &new->base;
|
||||
}
|
||||
|
||||
static void komeda_crtc_atomic_destroy_state(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
__drm_atomic_helper_crtc_destroy_state(state);
|
||||
kfree(to_kcrtc_st(state));
|
||||
}
|
||||
|
||||
static int komeda_crtc_vblank_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_dev *mdev = crtc->dev->dev_private;
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
|
||||
mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void komeda_crtc_vblank_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_dev *mdev = crtc->dev->dev_private;
|
||||
struct komeda_crtc *kcrtc = to_kcrtc(crtc);
|
||||
|
||||
mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, false);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs komeda_crtc_funcs = {
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = komeda_crtc_reset,
|
||||
.atomic_duplicate_state = komeda_crtc_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_crtc_atomic_destroy_state,
|
||||
.enable_vblank = komeda_crtc_vblank_enable,
|
||||
.disable_vblank = komeda_crtc_vblank_disable,
|
||||
};
|
||||
|
||||
int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
|
||||
|
|
|
@ -59,6 +59,48 @@ static void komeda_debugfs_init(struct komeda_dev *mdev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static ssize_t
|
||||
core_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct komeda_dev *mdev = dev_to_mdev(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", mdev->chip.core_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(core_id);
|
||||
|
||||
static ssize_t
|
||||
config_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct komeda_dev *mdev = dev_to_mdev(dev);
|
||||
struct komeda_pipeline *pipe = mdev->pipelines[0];
|
||||
union komeda_config_id config_id;
|
||||
int i;
|
||||
|
||||
memset(&config_id, 0, sizeof(config_id));
|
||||
|
||||
config_id.max_line_sz = pipe->layers[0]->hsize_in.end;
|
||||
config_id.n_pipelines = mdev->n_pipelines;
|
||||
config_id.n_scalers = pipe->n_scalers;
|
||||
config_id.n_layers = pipe->n_layers;
|
||||
config_id.n_richs = 0;
|
||||
for (i = 0; i < pipe->n_layers; i++) {
|
||||
if (pipe->layers[i]->layer_type == KOMEDA_FMT_RICH_LAYER)
|
||||
config_id.n_richs++;
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "0x%08x\n", config_id.value);
|
||||
}
|
||||
static DEVICE_ATTR_RO(config_id);
|
||||
|
||||
static struct attribute *komeda_sysfs_entries[] = {
|
||||
&dev_attr_core_id.attr,
|
||||
&dev_attr_config_id.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group komeda_sysfs_attr_group = {
|
||||
.attrs = komeda_sysfs_entries,
|
||||
};
|
||||
|
||||
static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
|
||||
{
|
||||
struct komeda_pipeline *pipe;
|
||||
|
@ -151,6 +193,8 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
|||
if (!mdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&mdev->lock);
|
||||
|
||||
mdev->dev = dev;
|
||||
mdev->reg_base = devm_ioremap_resource(dev, io_res);
|
||||
if (IS_ERR(mdev->reg_base)) {
|
||||
|
@ -205,6 +249,12 @@ struct komeda_dev *komeda_dev_create(struct device *dev)
|
|||
goto err_cleanup;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group);
|
||||
if (err) {
|
||||
DRM_ERROR("create sysfs group failed.\n");
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
komeda_debugfs_init(mdev);
|
||||
#endif
|
||||
|
@ -222,6 +272,8 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
|
|||
struct komeda_dev_funcs *funcs = mdev->funcs;
|
||||
int i;
|
||||
|
||||
sysfs_remove_group(&dev->kobj, &komeda_sysfs_attr_group);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
debugfs_remove_recursive(mdev->debugfs_root);
|
||||
#endif
|
||||
|
|
|
@ -103,9 +103,38 @@ struct komeda_dev_funcs {
|
|||
int (*enable_irq)(struct komeda_dev *mdev);
|
||||
/** @disable_irq: disable irq */
|
||||
int (*disable_irq)(struct komeda_dev *mdev);
|
||||
/** @on_off_vblank: notify HW to on/off vblank */
|
||||
void (*on_off_vblank)(struct komeda_dev *mdev,
|
||||
int master_pipe, bool on);
|
||||
|
||||
/** @dump_register: Optional, dump registers to seq_file */
|
||||
void (*dump_register)(struct komeda_dev *mdev, struct seq_file *seq);
|
||||
/**
|
||||
* @change_opmode:
|
||||
*
|
||||
* Notify HW to switch to a new display operation mode.
|
||||
*/
|
||||
int (*change_opmode)(struct komeda_dev *mdev, int new_mode);
|
||||
/** @flush: Notify the HW to flush or kickoff the update */
|
||||
void (*flush)(struct komeda_dev *mdev,
|
||||
int master_pipe, u32 active_pipes);
|
||||
};
|
||||
|
||||
/*
|
||||
* DISPLAY_MODE describes how many display been enabled, and which will be
|
||||
* passed to CHIP by &komeda_dev_funcs->change_opmode(), then CHIP can do the
|
||||
* pipeline resources assignment according to this usage hint.
|
||||
* - KOMEDA_MODE_DISP0: Only one display enabled, pipeline-0 work as master.
|
||||
* - KOMEDA_MODE_DISP1: Only one display enabled, pipeline-0 work as master.
|
||||
* - KOMEDA_MODE_DUAL_DISP: Dual display mode, both display has been enabled.
|
||||
* And D71 supports assign two pipelines to one single display on mode
|
||||
* KOMEDA_MODE_DISP0/DISP1
|
||||
*/
|
||||
enum {
|
||||
KOMEDA_MODE_INACTIVE = 0,
|
||||
KOMEDA_MODE_DISP0 = BIT(0),
|
||||
KOMEDA_MODE_DISP1 = BIT(1),
|
||||
KOMEDA_MODE_DUAL_DISP = KOMEDA_MODE_DISP0 | KOMEDA_MODE_DISP1,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -116,21 +145,31 @@ struct komeda_dev_funcs {
|
|||
* control-abilites of device.
|
||||
*/
|
||||
struct komeda_dev {
|
||||
/** @dev: the base device structure */
|
||||
struct device *dev;
|
||||
/** @reg_base: the base address of komeda io space */
|
||||
u32 __iomem *reg_base;
|
||||
|
||||
/** @chip: the basic chip information */
|
||||
struct komeda_chip_info chip;
|
||||
/** @fmt_tbl: initialized by &komeda_dev_funcs->init_format_table */
|
||||
struct komeda_format_caps_table fmt_tbl;
|
||||
/** @pclk: APB clock for register access */
|
||||
struct clk *pclk;
|
||||
/** @mck: HW main engine clk */
|
||||
/** @mclk: HW main engine clk */
|
||||
struct clk *mclk;
|
||||
|
||||
/** @irq: irq number */
|
||||
int irq;
|
||||
|
||||
/** @lock: used to protect dpmode */
|
||||
struct mutex lock;
|
||||
/** @dpmode: current display mode */
|
||||
u32 dpmode;
|
||||
|
||||
/** @n_pipelines: the number of pipe in @pipelines */
|
||||
int n_pipelines;
|
||||
/** @pipelines: the komeda pipelines */
|
||||
struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];
|
||||
|
||||
/** @funcs: chip funcs to access to HW */
|
||||
|
@ -143,6 +182,7 @@ struct komeda_dev {
|
|||
*/
|
||||
void *chip_data;
|
||||
|
||||
/** @debugfs_root: root directory of komeda debugfs */
|
||||
struct dentry *debugfs_root;
|
||||
};
|
||||
|
||||
|
@ -158,4 +198,6 @@ d71_identify(u32 __iomem *reg, struct komeda_chip_info *chip);
|
|||
struct komeda_dev *komeda_dev_create(struct device *dev);
|
||||
void komeda_dev_destroy(struct komeda_dev *mdev);
|
||||
|
||||
struct komeda_dev *dev_to_mdev(struct device *dev);
|
||||
|
||||
#endif /*_KOMEDA_DEV_H_*/
|
||||
|
|
|
@ -17,6 +17,13 @@ struct komeda_drv {
|
|||
struct komeda_kms_dev *kms;
|
||||
};
|
||||
|
||||
struct komeda_dev *dev_to_mdev(struct device *dev)
|
||||
{
|
||||
struct komeda_drv *mdrv = dev_get_drvdata(dev);
|
||||
|
||||
return mdrv ? mdrv->mdev : NULL;
|
||||
}
|
||||
|
||||
static void komeda_unbind(struct device *dev)
|
||||
{
|
||||
struct komeda_drv *mdrv = dev_get_drvdata(dev);
|
||||
|
@ -120,7 +127,7 @@ static const struct komeda_product_data komeda_products[] = {
|
|||
},
|
||||
};
|
||||
|
||||
const struct of_device_id komeda_of_match[] = {
|
||||
static const struct of_device_id komeda_of_match[] = {
|
||||
{ .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -10,11 +10,16 @@
|
|||
#include <drm/drm_framebuffer.h>
|
||||
#include "komeda_format_caps.h"
|
||||
|
||||
/** struct komeda_fb - entend drm_framebuffer with komeda attribute */
|
||||
/**
|
||||
* struct komeda_fb - Entending drm_framebuffer with komeda attribute
|
||||
*/
|
||||
struct komeda_fb {
|
||||
/** @base: &drm_framebuffer */
|
||||
struct drm_framebuffer base;
|
||||
/* @format_caps: &komeda_format_caps */
|
||||
/**
|
||||
* @format_caps:
|
||||
* extends drm_format_info for komeda specific information
|
||||
*/
|
||||
const struct komeda_format_caps *format_caps;
|
||||
/** @aligned_w: aligned frame buffer width */
|
||||
u32 aligned_w;
|
||||
|
|
|
@ -26,10 +26,10 @@ static int komeda_gem_cma_dumb_create(struct drm_file *file,
|
|||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
u32 alignment = 16; /* TODO get alignment from dev */
|
||||
struct komeda_dev *mdev = dev->dev_private;
|
||||
u32 pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
|
||||
args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8),
|
||||
alignment);
|
||||
args->pitch = ALIGN(pitch, mdev->chip.bus_width);
|
||||
|
||||
return drm_gem_cma_dumb_create_internal(file, dev, args);
|
||||
}
|
||||
|
@ -100,9 +100,37 @@ static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = {
|
|||
.atomic_commit_tail = komeda_kms_commit_tail,
|
||||
};
|
||||
|
||||
static int komeda_kms_check(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_st, *new_crtc_st;
|
||||
int i, err;
|
||||
|
||||
err = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* komeda need to re-calculate resource assumption in every commit
|
||||
* so need to add all affected_planes (even unchanged) to
|
||||
* drm_atomic_state.
|
||||
*/
|
||||
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_st, new_crtc_st, i) {
|
||||
err = drm_atomic_add_affected_planes(state, crtc);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = drm_atomic_helper_check_planes(dev, state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs komeda_mode_config_funcs = {
|
||||
.fb_create = komeda_fb_create,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_check = komeda_kms_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
|
@ -184,6 +212,7 @@ uninstall_irq:
|
|||
drm_irq_uninstall(drm);
|
||||
cleanup_mode_config:
|
||||
drm_mode_config_cleanup(drm);
|
||||
komeda_kms_cleanup_private_objs(kms);
|
||||
free_kms:
|
||||
kfree(kms);
|
||||
return ERR_PTR(err);
|
||||
|
@ -198,7 +227,7 @@ void komeda_kms_detach(struct komeda_kms_dev *kms)
|
|||
drm_dev_unregister(drm);
|
||||
drm_irq_uninstall(drm);
|
||||
component_unbind_all(mdev->dev, drm);
|
||||
komeda_kms_cleanup_private_objs(mdev);
|
||||
komeda_kms_cleanup_private_objs(kms);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm->dev_private = NULL;
|
||||
drm_dev_put(drm);
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#include <video/videomode.h>
|
||||
#include <video/display_timing.h>
|
||||
|
||||
/** struct komeda_plane - komeda instance of drm_plane */
|
||||
/**
|
||||
* struct komeda_plane - komeda instance of drm_plane
|
||||
*/
|
||||
struct komeda_plane {
|
||||
/** @base: &drm_plane */
|
||||
struct drm_plane base;
|
||||
|
@ -70,9 +72,14 @@ struct komeda_crtc {
|
|||
* merge into the master.
|
||||
*/
|
||||
struct komeda_pipeline *slave;
|
||||
|
||||
/** @disable_done: this flip_done is for tracing the disable */
|
||||
struct completion *disable_done;
|
||||
};
|
||||
|
||||
/** struct komeda_crtc_state */
|
||||
/**
|
||||
* struct komeda_crtc_state
|
||||
*/
|
||||
struct komeda_crtc_state {
|
||||
/** @base: &drm_crtc_state */
|
||||
struct drm_crtc_state base;
|
||||
|
@ -80,7 +87,15 @@ struct komeda_crtc_state {
|
|||
/* private properties */
|
||||
|
||||
/* computed state which are used by validate/check */
|
||||
/**
|
||||
* @affected_pipes:
|
||||
* the affected pipelines in once display instance
|
||||
*/
|
||||
u32 affected_pipes;
|
||||
/**
|
||||
* @active_pipes:
|
||||
* the active pipelines in once display instance
|
||||
*/
|
||||
u32 active_pipes;
|
||||
};
|
||||
|
||||
|
@ -108,7 +123,7 @@ int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
|
|||
int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
|
||||
int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
|
||||
struct komeda_dev *mdev);
|
||||
void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev);
|
||||
void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms);
|
||||
|
||||
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
||||
struct komeda_events *evts);
|
||||
|
|
|
@ -62,7 +62,7 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev,
|
|||
devm_kfree(mdev->dev, pipe);
|
||||
}
|
||||
|
||||
struct komeda_component **
|
||||
static struct komeda_component **
|
||||
komeda_pipeline_get_component_pos(struct komeda_pipeline *pipe, int id)
|
||||
{
|
||||
struct komeda_dev *mdev = pipe->mdev;
|
||||
|
|
|
@ -90,32 +90,35 @@ struct komeda_component {
|
|||
u32 __iomem *reg;
|
||||
/** @id: component id */
|
||||
u32 id;
|
||||
/** @hw_ic: component hw id,
|
||||
* which is initialized by chip and used by chip only
|
||||
/**
|
||||
* @hw_id: component hw id,
|
||||
* which is initialized by chip and used by chip only
|
||||
*/
|
||||
u32 hw_id;
|
||||
|
||||
/**
|
||||
* @max_active_inputs:
|
||||
* @max_active_outpus:
|
||||
* @max_active_outputs:
|
||||
*
|
||||
* maximum number of inputs/outputs that can be active in the same time
|
||||
* maximum number of inputs/outputs that can be active at the same time
|
||||
* Note:
|
||||
* the number isn't the bit number of @supported_inputs or
|
||||
* @supported_outputs, but may be less than it, since component may not
|
||||
* support enabling all @supported_inputs/outputs at the same time.
|
||||
*/
|
||||
u8 max_active_inputs;
|
||||
/** @max_active_outputs: maximum number of outputs */
|
||||
u8 max_active_outputs;
|
||||
/**
|
||||
* @supported_inputs:
|
||||
* @supported_outputs:
|
||||
*
|
||||
* bitmask of BIT(component->id) for the supported inputs/outputs
|
||||
* bitmask of BIT(component->id) for the supported inputs/outputs,
|
||||
* describes the possibilities of how a component is linked into a
|
||||
* pipeline.
|
||||
*/
|
||||
u32 supported_inputs;
|
||||
/** @supported_outputs: bitmask of supported output componenet ids */
|
||||
u32 supported_outputs;
|
||||
|
||||
/**
|
||||
|
@ -134,7 +137,8 @@ struct komeda_component {
|
|||
struct komeda_component_output {
|
||||
/** @component: indicate which component the data comes from */
|
||||
struct komeda_component *component;
|
||||
/** @output_port:
|
||||
/**
|
||||
* @output_port:
|
||||
* the output port of the &komeda_component_output.component
|
||||
*/
|
||||
u8 output_port;
|
||||
|
@ -150,11 +154,12 @@ struct komeda_component_output {
|
|||
struct komeda_component_state {
|
||||
/** @obj: tracking component_state by drm_atomic_state */
|
||||
struct drm_private_state obj;
|
||||
/** @component: backpointer to the component */
|
||||
struct komeda_component *component;
|
||||
/**
|
||||
* @binding_user:
|
||||
* currently bound user, the user can be crtc/plane/wb_conn, which is
|
||||
* valid decided by @component and @inputs
|
||||
* currently bound user, the user can be @crtc, @plane or @wb_conn,
|
||||
* which is valid decided by @component and @inputs
|
||||
*
|
||||
* - Layer: its user always is plane.
|
||||
* - compiz/improc/timing_ctrlr: the user is crtc.
|
||||
|
@ -162,20 +167,24 @@ struct komeda_component_state {
|
|||
* - scaler: plane when input is layer, wb_conn if input is compiz.
|
||||
*/
|
||||
union {
|
||||
/** @crtc: backpointer for user crtc */
|
||||
struct drm_crtc *crtc;
|
||||
/** @plane: backpointer for user plane */
|
||||
struct drm_plane *plane;
|
||||
/** @wb_conn: backpointer for user wb_connector */
|
||||
struct drm_connector *wb_conn;
|
||||
void *binding_user;
|
||||
};
|
||||
|
||||
/**
|
||||
* @active_inputs:
|
||||
*
|
||||
* active_inputs is bitmask of @inputs index
|
||||
*
|
||||
* - active_inputs = changed_active_inputs + unchanged_active_inputs
|
||||
* - affected_inputs = old->active_inputs + new->active_inputs;
|
||||
* - active_inputs = changed_active_inputs | unchanged_active_inputs
|
||||
* - affected_inputs = old->active_inputs | new->active_inputs;
|
||||
* - disabling_inputs = affected_inputs ^ active_inputs;
|
||||
* - changed_inputs = disabling_inputs + changed_active_inputs;
|
||||
* - changed_inputs = disabling_inputs | changed_active_inputs;
|
||||
*
|
||||
* NOTE:
|
||||
* changed_inputs doesn't include all active_input but only
|
||||
|
@ -183,7 +192,9 @@ struct komeda_component_state {
|
|||
* level for dirty update.
|
||||
*/
|
||||
u16 active_inputs;
|
||||
/** @changed_active_inputs: bitmask of the changed @active_inputs */
|
||||
u16 changed_active_inputs;
|
||||
/** @affected_inputs: bitmask for affected @inputs */
|
||||
u16 affected_inputs;
|
||||
/**
|
||||
* @inputs:
|
||||
|
@ -278,6 +289,22 @@ struct komeda_timing_ctrlr_state {
|
|||
struct komeda_component_state base;
|
||||
};
|
||||
|
||||
/* Why define A separated structure but not use plane_state directly ?
|
||||
* 1. Komeda supports layer_split which means a plane_state can be split and
|
||||
* handled by two layers, one layer only handle half of plane image.
|
||||
* 2. Fix up the user properties according to HW's capabilities, like user
|
||||
* set rotation to R180, but HW only supports REFLECT_X+Y. the rot here is
|
||||
* after drm_rotation_simplify()
|
||||
*/
|
||||
struct komeda_data_flow_cfg {
|
||||
struct komeda_component_output input;
|
||||
u16 in_x, in_y, in_w, in_h;
|
||||
u32 out_x, out_y, out_w, out_h;
|
||||
u32 rot;
|
||||
int blending_zorder;
|
||||
u8 pixel_blend_mode, layer_alpha;
|
||||
};
|
||||
|
||||
/** struct komeda_pipeline_funcs */
|
||||
struct komeda_pipeline_funcs {
|
||||
/* dump_register: Optional, dump registers to seq_file */
|
||||
|
@ -303,14 +330,23 @@ struct komeda_pipeline {
|
|||
int id;
|
||||
/** @avail_comps: available components mask of pipeline */
|
||||
u32 avail_comps;
|
||||
/** @n_layers: the number of layer on @layers */
|
||||
int n_layers;
|
||||
/** @layers: the pipeline layers */
|
||||
struct komeda_layer *layers[KOMEDA_PIPELINE_MAX_LAYERS];
|
||||
/** @n_scalers: the number of scaler on @scalers */
|
||||
int n_scalers;
|
||||
/** @scalers: the pipeline scalers */
|
||||
struct komeda_scaler *scalers[KOMEDA_PIPELINE_MAX_SCALERS];
|
||||
/** @compiz: compositor */
|
||||
struct komeda_compiz *compiz;
|
||||
/** @wb_layer: writeback layer */
|
||||
struct komeda_layer *wb_layer;
|
||||
/** @improc: post image processor */
|
||||
struct komeda_improc *improc;
|
||||
/** @ctrlr: timing controller */
|
||||
struct komeda_timing_ctrlr *ctrlr;
|
||||
/** @funcs: chip pipeline functions */
|
||||
struct komeda_pipeline_funcs *funcs; /* private pipeline functions */
|
||||
|
||||
/** @of_node: pipeline dt node */
|
||||
|
@ -331,6 +367,7 @@ struct komeda_pipeline {
|
|||
struct komeda_pipeline_state {
|
||||
/** @obj: tracking pipeline_state by drm_atomic_state */
|
||||
struct drm_private_state obj;
|
||||
/** @pipe: backpointer to the pipeline */
|
||||
struct komeda_pipeline *pipe;
|
||||
/** @crtc: currently bound crtc */
|
||||
struct drm_crtc *crtc;
|
||||
|
@ -382,4 +419,26 @@ komeda_component_add(struct komeda_pipeline *pipe,
|
|||
void komeda_component_destroy(struct komeda_dev *mdev,
|
||||
struct komeda_component *c);
|
||||
|
||||
struct komeda_plane_state;
|
||||
struct komeda_crtc_state;
|
||||
struct komeda_crtc;
|
||||
|
||||
int komeda_build_layer_data_flow(struct komeda_layer *layer,
|
||||
struct komeda_plane_state *kplane_st,
|
||||
struct komeda_crtc_state *kcrtc_st,
|
||||
struct komeda_data_flow_cfg *dflow);
|
||||
int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
|
||||
struct komeda_crtc_state *kcrtc_st);
|
||||
|
||||
int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
|
||||
struct komeda_crtc_state *kcrtc_st);
|
||||
|
||||
struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_old_state(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state);
|
||||
void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state);
|
||||
void komeda_pipeline_update(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state);
|
||||
|
||||
#endif /* _KOMEDA_PIPELINE_H_*/
|
||||
|
|
|
@ -0,0 +1,610 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
|
||||
* Author: James.Qian.Wang <james.qian.wang@arm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drm_print.h>
|
||||
#include <linux/clk.h>
|
||||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
#include "komeda_pipeline.h"
|
||||
#include "komeda_framebuffer.h"
|
||||
|
||||
static inline bool is_switching_user(void *old, void *new)
|
||||
{
|
||||
if (!old || !new)
|
||||
return false;
|
||||
|
||||
return old != new;
|
||||
}
|
||||
|
||||
static struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_state(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_private_state *priv_st;
|
||||
|
||||
priv_st = drm_atomic_get_private_obj_state(state, &pipe->obj);
|
||||
if (IS_ERR(priv_st))
|
||||
return ERR_CAST(priv_st);
|
||||
|
||||
return priv_to_pipe_st(priv_st);
|
||||
}
|
||||
|
||||
struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_old_state(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_private_state *priv_st;
|
||||
|
||||
priv_st = drm_atomic_get_old_private_obj_state(state, &pipe->obj);
|
||||
if (priv_st)
|
||||
return priv_to_pipe_st(priv_st);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_new_state(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_private_state *priv_st;
|
||||
|
||||
priv_st = drm_atomic_get_new_private_obj_state(state, &pipe->obj);
|
||||
if (priv_st)
|
||||
return priv_to_pipe_st(priv_st);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Assign pipeline for crtc */
|
||||
static struct komeda_pipeline_state *
|
||||
komeda_pipeline_get_state_and_set_crtc(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *state,
|
||||
struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_pipeline_state *st;
|
||||
|
||||
st = komeda_pipeline_get_state(pipe, state);
|
||||
if (IS_ERR(st))
|
||||
return st;
|
||||
|
||||
if (is_switching_user(crtc, st->crtc)) {
|
||||
DRM_DEBUG_ATOMIC("CRTC%d required pipeline%d is busy.\n",
|
||||
drm_crtc_index(crtc), pipe->id);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
/* pipeline only can be disabled when the it is free or unused */
|
||||
if (!crtc && st->active_comps) {
|
||||
DRM_DEBUG_ATOMIC("Disabling a busy pipeline:%d.\n", pipe->id);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
st->crtc = crtc;
|
||||
|
||||
if (crtc) {
|
||||
struct komeda_crtc_state *kcrtc_st;
|
||||
|
||||
kcrtc_st = to_kcrtc_st(drm_atomic_get_new_crtc_state(state,
|
||||
crtc));
|
||||
|
||||
kcrtc_st->active_pipes |= BIT(pipe->id);
|
||||
kcrtc_st->affected_pipes |= BIT(pipe->id);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
static struct komeda_component_state *
|
||||
komeda_component_get_state(struct komeda_component *c,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_private_state *priv_st;
|
||||
|
||||
WARN_ON(!drm_modeset_is_locked(&c->pipeline->obj.lock));
|
||||
|
||||
priv_st = drm_atomic_get_private_obj_state(state, &c->obj);
|
||||
if (IS_ERR(priv_st))
|
||||
return ERR_CAST(priv_st);
|
||||
|
||||
return priv_to_comp_st(priv_st);
|
||||
}
|
||||
|
||||
static struct komeda_component_state *
|
||||
komeda_component_get_old_state(struct komeda_component *c,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_private_state *priv_st;
|
||||
|
||||
priv_st = drm_atomic_get_old_private_obj_state(state, &c->obj);
|
||||
if (priv_st)
|
||||
return priv_to_comp_st(priv_st);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* komeda_component_get_state_and_set_user()
|
||||
*
|
||||
* @c: component to get state and set user
|
||||
* @state: global atomic state
|
||||
* @user: direct user, the binding user
|
||||
* @crtc: the CRTC user, the big boss :)
|
||||
*
|
||||
* This function accepts two users:
|
||||
* - The direct user: can be plane/crtc/wb_connector depends on component
|
||||
* - The big boss (CRTC)
|
||||
* CRTC is the big boss (the final user), because all component resources
|
||||
* eventually will be assigned to CRTC, like the layer will be binding to
|
||||
* kms_plane, but kms plane will be binding to a CRTC eventually.
|
||||
*
|
||||
* The big boss (CRTC) is for pipeline assignment, since &komeda_component isn't
|
||||
* independent and can be assigned to CRTC freely, but belongs to a specific
|
||||
* pipeline, only pipeline can be shared between crtc, and pipeline as a whole
|
||||
* (include all the internal components) assigned to a specific CRTC.
|
||||
*
|
||||
* So when set a user to komeda_component, need first to check the status of
|
||||
* component->pipeline to see if the pipeline is available on this specific
|
||||
* CRTC. if the pipeline is busy (assigned to another CRTC), even the required
|
||||
* component is free, the component still cannot be assigned to the direct user.
|
||||
*/
|
||||
static struct komeda_component_state *
|
||||
komeda_component_get_state_and_set_user(struct komeda_component *c,
|
||||
struct drm_atomic_state *state,
|
||||
void *user,
|
||||
struct drm_crtc *crtc)
|
||||
{
|
||||
struct komeda_pipeline_state *pipe_st;
|
||||
struct komeda_component_state *st;
|
||||
|
||||
/* First check if the pipeline is available */
|
||||
pipe_st = komeda_pipeline_get_state_and_set_crtc(c->pipeline,
|
||||
state, crtc);
|
||||
if (IS_ERR(pipe_st))
|
||||
return ERR_CAST(pipe_st);
|
||||
|
||||
st = komeda_component_get_state(c, state);
|
||||
if (IS_ERR(st))
|
||||
return st;
|
||||
|
||||
/* check if the component has been occupied */
|
||||
if (is_switching_user(user, st->binding_user)) {
|
||||
DRM_DEBUG_ATOMIC("required %s is busy.\n", c->name);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
st->binding_user = user;
|
||||
/* mark the component as active if user is valid */
|
||||
if (st->binding_user)
|
||||
pipe_st->active_comps |= BIT(c->id);
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_component_add_input(struct komeda_component_state *state,
|
||||
struct komeda_component_output *input,
|
||||
int idx)
|
||||
{
|
||||
struct komeda_component *c = state->component;
|
||||
|
||||
WARN_ON((idx < 0 || idx >= c->max_active_inputs));
|
||||
|
||||
/* since the inputs[i] is only valid when it is active. So if a input[i]
|
||||
* is a newly enabled input which switches from disable to enable, then
|
||||
* the old inputs[i] is undefined (NOT zeroed), we can not rely on
|
||||
* memcmp, but directly mark it changed
|
||||
*/
|
||||
if (!has_bit(idx, state->affected_inputs) ||
|
||||
memcmp(&state->inputs[idx], input, sizeof(*input))) {
|
||||
memcpy(&state->inputs[idx], input, sizeof(*input));
|
||||
state->changed_active_inputs |= BIT(idx);
|
||||
}
|
||||
state->active_inputs |= BIT(idx);
|
||||
state->affected_inputs |= BIT(idx);
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_component_check_input(struct komeda_component_state *state,
|
||||
struct komeda_component_output *input,
|
||||
int idx)
|
||||
{
|
||||
struct komeda_component *c = state->component;
|
||||
|
||||
if ((idx < 0) || (idx >= c->max_active_inputs)) {
|
||||
DRM_DEBUG_ATOMIC("%s invalid input id: %d.\n", c->name, idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (has_bit(idx, state->active_inputs)) {
|
||||
DRM_DEBUG_ATOMIC("%s required input_id: %d has been occupied already.\n",
|
||||
c->name, idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_component_set_output(struct komeda_component_output *output,
|
||||
struct komeda_component *comp,
|
||||
u8 output_port)
|
||||
{
|
||||
output->component = comp;
|
||||
output->output_port = output_port;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_component_validate_private(struct komeda_component *c,
|
||||
struct komeda_component_state *st)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!c->funcs->validate)
|
||||
return 0;
|
||||
|
||||
err = c->funcs->validate(c, st);
|
||||
if (err)
|
||||
DRM_DEBUG_ATOMIC("%s validate private failed.\n", c->name);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_layer_check_cfg(struct komeda_layer *layer,
|
||||
struct komeda_plane_state *kplane_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
if (!in_range(&layer->hsize_in, dflow->in_w)) {
|
||||
DRM_DEBUG_ATOMIC("src_w: %d is out of range.\n", dflow->in_w);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!in_range(&layer->vsize_in, dflow->in_h)) {
|
||||
DRM_DEBUG_ATOMIC("src_h: %d is out of range.\n", dflow->in_h);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_layer_validate(struct komeda_layer *layer,
|
||||
struct komeda_plane_state *kplane_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_plane_state *plane_st = &kplane_st->base;
|
||||
struct drm_framebuffer *fb = plane_st->fb;
|
||||
struct komeda_fb *kfb = to_kfb(fb);
|
||||
struct komeda_component_state *c_st;
|
||||
struct komeda_layer_state *st;
|
||||
int i, err;
|
||||
|
||||
err = komeda_layer_check_cfg(layer, kplane_st, dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
c_st = komeda_component_get_state_and_set_user(&layer->base,
|
||||
plane_st->state, plane_st->plane, plane_st->crtc);
|
||||
if (IS_ERR(c_st))
|
||||
return PTR_ERR(c_st);
|
||||
|
||||
st = to_layer_st(c_st);
|
||||
|
||||
st->rot = dflow->rot;
|
||||
st->hsize = kfb->aligned_w;
|
||||
st->vsize = kfb->aligned_h;
|
||||
|
||||
for (i = 0; i < fb->format->num_planes; i++)
|
||||
st->addr[i] = komeda_fb_get_pixel_addr(kfb, dflow->in_x,
|
||||
dflow->in_y, i);
|
||||
|
||||
err = komeda_component_validate_private(&layer->base, c_st);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* update the data flow for the next stage */
|
||||
komeda_component_set_output(&dflow->input, &layer->base, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pipeline_composition_size(struct komeda_crtc_state *kcrtc_st,
|
||||
u16 *hsize, u16 *vsize)
|
||||
{
|
||||
struct drm_display_mode *m = &kcrtc_st->base.adjusted_mode;
|
||||
|
||||
if (hsize)
|
||||
*hsize = m->hdisplay;
|
||||
if (vsize)
|
||||
*vsize = m->vdisplay;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_compiz_set_input(struct komeda_compiz *compiz,
|
||||
struct komeda_crtc_state *kcrtc_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_atomic_state *drm_st = kcrtc_st->base.state;
|
||||
struct komeda_component_state *c_st, *old_st;
|
||||
struct komeda_compiz_input_cfg *cin;
|
||||
u16 compiz_w, compiz_h;
|
||||
int idx = dflow->blending_zorder;
|
||||
|
||||
pipeline_composition_size(kcrtc_st, &compiz_w, &compiz_h);
|
||||
/* check display rect */
|
||||
if ((dflow->out_x + dflow->out_w > compiz_w) ||
|
||||
(dflow->out_y + dflow->out_h > compiz_h) ||
|
||||
dflow->out_w == 0 || dflow->out_h == 0) {
|
||||
DRM_DEBUG_ATOMIC("invalid disp rect [x=%d, y=%d, w=%d, h=%d]\n",
|
||||
dflow->out_x, dflow->out_y,
|
||||
dflow->out_w, dflow->out_h);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c_st = komeda_component_get_state_and_set_user(&compiz->base, drm_st,
|
||||
kcrtc_st->base.crtc, kcrtc_st->base.crtc);
|
||||
if (IS_ERR(c_st))
|
||||
return PTR_ERR(c_st);
|
||||
|
||||
if (komeda_component_check_input(c_st, &dflow->input, idx))
|
||||
return -EINVAL;
|
||||
|
||||
cin = &(to_compiz_st(c_st)->cins[idx]);
|
||||
|
||||
cin->hsize = dflow->out_w;
|
||||
cin->vsize = dflow->out_h;
|
||||
cin->hoffset = dflow->out_x;
|
||||
cin->voffset = dflow->out_y;
|
||||
cin->pixel_blend_mode = dflow->pixel_blend_mode;
|
||||
cin->layer_alpha = dflow->layer_alpha;
|
||||
|
||||
old_st = komeda_component_get_old_state(&compiz->base, drm_st);
|
||||
WARN_ON(!old_st);
|
||||
|
||||
/* compare with old to check if this input has been changed */
|
||||
if (memcmp(&(to_compiz_st(old_st)->cins[idx]), cin, sizeof(*cin)))
|
||||
c_st->changed_active_inputs |= BIT(idx);
|
||||
|
||||
komeda_component_add_input(c_st, &dflow->input, idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_compiz_validate(struct komeda_compiz *compiz,
|
||||
struct komeda_crtc_state *state,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct komeda_component_state *c_st;
|
||||
struct komeda_compiz_state *st;
|
||||
|
||||
c_st = komeda_component_get_state_and_set_user(&compiz->base,
|
||||
state->base.state, state->base.crtc, state->base.crtc);
|
||||
if (IS_ERR(c_st))
|
||||
return PTR_ERR(c_st);
|
||||
|
||||
st = to_compiz_st(c_st);
|
||||
|
||||
pipeline_composition_size(state, &st->hsize, &st->vsize);
|
||||
|
||||
komeda_component_set_output(&dflow->input, &compiz->base, 0);
|
||||
|
||||
/* compiz output dflow will be fed to the next pipeline stage, prepare
|
||||
* the data flow configuration for the next stage
|
||||
*/
|
||||
if (dflow) {
|
||||
dflow->in_w = st->hsize;
|
||||
dflow->in_h = st->vsize;
|
||||
dflow->out_w = dflow->in_w;
|
||||
dflow->out_h = dflow->in_h;
|
||||
/* the output data of compiz doesn't have alpha, it only can be
|
||||
* used as bottom layer when blend it with master layers
|
||||
*/
|
||||
dflow->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
|
||||
dflow->layer_alpha = 0xFF;
|
||||
dflow->blending_zorder = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_improc_validate(struct komeda_improc *improc,
|
||||
struct komeda_crtc_state *kcrtc_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_crtc *crtc = kcrtc_st->base.crtc;
|
||||
struct komeda_component_state *c_st;
|
||||
struct komeda_improc_state *st;
|
||||
|
||||
c_st = komeda_component_get_state_and_set_user(&improc->base,
|
||||
kcrtc_st->base.state, crtc, crtc);
|
||||
if (IS_ERR(c_st))
|
||||
return PTR_ERR(c_st);
|
||||
|
||||
st = to_improc_st(c_st);
|
||||
|
||||
st->hsize = dflow->in_w;
|
||||
st->vsize = dflow->in_h;
|
||||
|
||||
komeda_component_add_input(&st->base, &dflow->input, 0);
|
||||
komeda_component_set_output(&dflow->input, &improc->base, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
komeda_timing_ctrlr_validate(struct komeda_timing_ctrlr *ctrlr,
|
||||
struct komeda_crtc_state *kcrtc_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_crtc *crtc = kcrtc_st->base.crtc;
|
||||
struct komeda_timing_ctrlr_state *st;
|
||||
struct komeda_component_state *c_st;
|
||||
|
||||
c_st = komeda_component_get_state_and_set_user(&ctrlr->base,
|
||||
kcrtc_st->base.state, crtc, crtc);
|
||||
if (IS_ERR(c_st))
|
||||
return PTR_ERR(c_st);
|
||||
|
||||
st = to_ctrlr_st(c_st);
|
||||
|
||||
komeda_component_add_input(&st->base, &dflow->input, 0);
|
||||
komeda_component_set_output(&dflow->input, &ctrlr->base, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int komeda_build_layer_data_flow(struct komeda_layer *layer,
|
||||
struct komeda_plane_state *kplane_st,
|
||||
struct komeda_crtc_state *kcrtc_st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_plane *plane = kplane_st->base.plane;
|
||||
struct komeda_pipeline *pipe = layer->base.pipeline;
|
||||
int err;
|
||||
|
||||
DRM_DEBUG_ATOMIC("%s handling [PLANE:%d:%s]: src[x/y:%d/%d, w/h:%d/%d] disp[x/y:%d/%d, w/h:%d/%d]",
|
||||
layer->base.name, plane->base.id, plane->name,
|
||||
dflow->in_x, dflow->in_y, dflow->in_w, dflow->in_h,
|
||||
dflow->out_x, dflow->out_y, dflow->out_w, dflow->out_h);
|
||||
|
||||
err = komeda_layer_validate(layer, kplane_st, dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_compiz_set_input(pipe->compiz, kcrtc_st, dflow);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* build display output data flow, the data path is:
|
||||
* compiz -> improc -> timing_ctrlr
|
||||
*/
|
||||
int komeda_build_display_data_flow(struct komeda_crtc *kcrtc,
|
||||
struct komeda_crtc_state *kcrtc_st)
|
||||
{
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct komeda_data_flow_cfg m_dflow; /* master data flow */
|
||||
int err;
|
||||
|
||||
memset(&m_dflow, 0, sizeof(m_dflow));
|
||||
|
||||
err = komeda_compiz_validate(master->compiz, kcrtc_st, &m_dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_improc_validate(master->improc, kcrtc_st, &m_dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_timing_ctrlr_validate(master->ctrlr, kcrtc_st, &m_dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_pipeline_unbound_components(struct komeda_pipeline *pipe,
|
||||
struct komeda_pipeline_state *new)
|
||||
{
|
||||
struct drm_atomic_state *drm_st = new->obj.state;
|
||||
struct komeda_pipeline_state *old = priv_to_pipe_st(pipe->obj.state);
|
||||
struct komeda_component_state *c_st;
|
||||
struct komeda_component *c;
|
||||
u32 disabling_comps, id;
|
||||
|
||||
WARN_ON(!old);
|
||||
|
||||
disabling_comps = (~new->active_comps) & old->active_comps;
|
||||
|
||||
/* unbound all disabling component */
|
||||
dp_for_each_set_bit(id, disabling_comps) {
|
||||
c = komeda_pipeline_get_component(pipe, id);
|
||||
c_st = komeda_component_get_state_and_set_user(c,
|
||||
drm_st, NULL, new->crtc);
|
||||
WARN_ON(IS_ERR(c_st));
|
||||
}
|
||||
}
|
||||
|
||||
/* release unclaimed pipeline resource */
|
||||
int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe,
|
||||
struct komeda_crtc_state *kcrtc_st)
|
||||
{
|
||||
struct drm_atomic_state *drm_st = kcrtc_st->base.state;
|
||||
struct komeda_pipeline_state *st;
|
||||
|
||||
/* ignore the pipeline which is not affected */
|
||||
if (!pipe || !has_bit(pipe->id, kcrtc_st->affected_pipes))
|
||||
return 0;
|
||||
|
||||
if (has_bit(pipe->id, kcrtc_st->active_pipes))
|
||||
st = komeda_pipeline_get_new_state(pipe, drm_st);
|
||||
else
|
||||
st = komeda_pipeline_get_state_and_set_crtc(pipe, drm_st, NULL);
|
||||
|
||||
if (WARN_ON(IS_ERR_OR_NULL(st)))
|
||||
return -EINVAL;
|
||||
|
||||
komeda_pipeline_unbound_components(pipe, st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void komeda_pipeline_disable(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct komeda_pipeline_state *old;
|
||||
struct komeda_component *c;
|
||||
struct komeda_component_state *c_st;
|
||||
u32 id, disabling_comps = 0;
|
||||
|
||||
old = komeda_pipeline_get_old_state(pipe, old_state);
|
||||
|
||||
disabling_comps = old->active_comps;
|
||||
DRM_DEBUG_ATOMIC("PIPE%d: disabling_comps: 0x%x.\n",
|
||||
pipe->id, disabling_comps);
|
||||
|
||||
dp_for_each_set_bit(id, disabling_comps) {
|
||||
c = komeda_pipeline_get_component(pipe, id);
|
||||
c_st = priv_to_comp_st(c->obj.state);
|
||||
|
||||
/*
|
||||
* If we disabled a component then all active_inputs should be
|
||||
* put in the list of changed_active_inputs, so they get
|
||||
* re-enabled.
|
||||
* This usually happens during a modeset when the pipeline is
|
||||
* first disabled and then the actual state gets committed
|
||||
* again.
|
||||
*/
|
||||
c_st->changed_active_inputs |= c_st->active_inputs;
|
||||
|
||||
c->funcs->disable(c);
|
||||
}
|
||||
}
|
||||
|
||||
void komeda_pipeline_update(struct komeda_pipeline *pipe,
|
||||
struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct komeda_pipeline_state *new = priv_to_pipe_st(pipe->obj.state);
|
||||
struct komeda_pipeline_state *old;
|
||||
struct komeda_component *c;
|
||||
u32 id, changed_comps = 0;
|
||||
|
||||
old = komeda_pipeline_get_old_state(pipe, old_state);
|
||||
|
||||
changed_comps = new->active_comps | old->active_comps;
|
||||
|
||||
DRM_DEBUG_ATOMIC("PIPE%d: active_comps: 0x%x, changed: 0x%x.\n",
|
||||
pipe->id, new->active_comps, changed_comps);
|
||||
|
||||
dp_for_each_set_bit(id, changed_comps) {
|
||||
c = komeda_pipeline_get_component(pipe, id);
|
||||
|
||||
if (new->active_comps & BIT(c->id))
|
||||
c->funcs->update(c, priv_to_comp_st(c->obj.state));
|
||||
else
|
||||
c->funcs->disable(c);
|
||||
}
|
||||
}
|
|
@ -7,10 +7,96 @@
|
|||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
|
||||
static int
|
||||
komeda_plane_init_data_flow(struct drm_plane_state *st,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_framebuffer *fb = st->fb;
|
||||
|
||||
memset(dflow, 0, sizeof(*dflow));
|
||||
|
||||
dflow->blending_zorder = st->zpos;
|
||||
|
||||
/* if format doesn't have alpha, fix blend mode to PIXEL_NONE */
|
||||
dflow->pixel_blend_mode = fb->format->has_alpha ?
|
||||
st->pixel_blend_mode : DRM_MODE_BLEND_PIXEL_NONE;
|
||||
dflow->layer_alpha = st->alpha >> 8;
|
||||
|
||||
dflow->out_x = st->crtc_x;
|
||||
dflow->out_y = st->crtc_y;
|
||||
dflow->out_w = st->crtc_w;
|
||||
dflow->out_h = st->crtc_h;
|
||||
|
||||
dflow->in_x = st->src_x >> 16;
|
||||
dflow->in_y = st->src_y >> 16;
|
||||
dflow->in_w = st->src_w >> 16;
|
||||
dflow->in_h = st->src_h >> 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* komeda_plane_atomic_check - build input data flow
|
||||
* @plane: DRM plane
|
||||
* @state: the plane state object
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero for success or -errno
|
||||
*/
|
||||
static int
|
||||
komeda_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct komeda_plane *kplane = to_kplane(plane);
|
||||
struct komeda_plane_state *kplane_st = to_kplane_st(state);
|
||||
struct komeda_layer *layer = kplane->layer;
|
||||
struct drm_crtc_state *crtc_st;
|
||||
struct komeda_crtc *kcrtc;
|
||||
struct komeda_crtc_state *kcrtc_st;
|
||||
struct komeda_data_flow_cfg dflow;
|
||||
int err;
|
||||
|
||||
if (!state->crtc || !state->fb)
|
||||
return 0;
|
||||
|
||||
crtc_st = drm_atomic_get_crtc_state(state->state, state->crtc);
|
||||
if (!crtc_st->enable) {
|
||||
DRM_DEBUG_ATOMIC("Cannot update plane on a disabled CRTC.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* crtc is inactive, skip the resource assignment */
|
||||
if (!crtc_st->active)
|
||||
return 0;
|
||||
|
||||
kcrtc = to_kcrtc(state->crtc);
|
||||
kcrtc_st = to_kcrtc_st(crtc_st);
|
||||
|
||||
err = komeda_plane_init_data_flow(state, &dflow);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_build_layer_data_flow(layer, kplane_st, kcrtc_st, &dflow);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* plane doesn't represent a real HW, so there is no HW update for plane.
|
||||
* komeda handles all the HW update in crtc->atomic_flush
|
||||
*/
|
||||
static void
|
||||
komeda_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs komeda_plane_helper_funcs = {
|
||||
.atomic_check = komeda_plane_atomic_check,
|
||||
.atomic_update = komeda_plane_atomic_update,
|
||||
};
|
||||
|
||||
static void komeda_plane_destroy(struct drm_plane *plane)
|
||||
|
@ -20,7 +106,60 @@ static void komeda_plane_destroy(struct drm_plane *plane)
|
|||
kfree(to_kplane(plane));
|
||||
}
|
||||
|
||||
static void komeda_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct komeda_plane_state *state;
|
||||
struct komeda_plane *kplane = to_kplane(plane);
|
||||
|
||||
if (plane->state)
|
||||
__drm_atomic_helper_plane_destroy_state(plane->state);
|
||||
|
||||
kfree(plane->state);
|
||||
plane->state = NULL;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state) {
|
||||
state->base.rotation = DRM_MODE_ROTATE_0;
|
||||
state->base.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
|
||||
state->base.alpha = DRM_BLEND_ALPHA_OPAQUE;
|
||||
state->base.zpos = kplane->layer->base.id;
|
||||
plane->state = &state->base;
|
||||
plane->state->plane = plane;
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
komeda_plane_atomic_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct komeda_plane_state *new;
|
||||
|
||||
if (WARN_ON(!plane->state))
|
||||
return NULL;
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, &new->base);
|
||||
|
||||
return &new->base;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_plane_atomic_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
__drm_atomic_helper_plane_destroy_state(state);
|
||||
kfree(to_kplane_st(state));
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs komeda_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = komeda_plane_destroy,
|
||||
.reset = komeda_plane_reset,
|
||||
.atomic_duplicate_state = komeda_plane_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_plane_atomic_destroy_state,
|
||||
};
|
||||
|
||||
/* for komeda, which is pipeline can be share between crtcs */
|
||||
|
|
|
@ -7,6 +7,188 @@
|
|||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
|
||||
static void
|
||||
komeda_component_state_reset(struct komeda_component_state *st)
|
||||
{
|
||||
st->binding_user = NULL;
|
||||
st->affected_inputs = st->active_inputs;
|
||||
st->active_inputs = 0;
|
||||
st->changed_active_inputs = 0;
|
||||
}
|
||||
|
||||
static struct drm_private_state *
|
||||
komeda_layer_atomic_duplicate_state(struct drm_private_obj *obj)
|
||||
{
|
||||
struct komeda_layer_state *st;
|
||||
|
||||
st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
komeda_component_state_reset(&st->base);
|
||||
__drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj);
|
||||
|
||||
return &st->base.obj;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_layer_atomic_destroy_state(struct drm_private_obj *obj,
|
||||
struct drm_private_state *state)
|
||||
{
|
||||
struct komeda_layer_state *st = to_layer_st(priv_to_comp_st(state));
|
||||
|
||||
kfree(st);
|
||||
}
|
||||
|
||||
static const struct drm_private_state_funcs komeda_layer_obj_funcs = {
|
||||
.atomic_duplicate_state = komeda_layer_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_layer_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static int komeda_layer_obj_add(struct komeda_kms_dev *kms,
|
||||
struct komeda_layer *layer)
|
||||
{
|
||||
struct komeda_layer_state *st;
|
||||
|
||||
st = kzalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->base.component = &layer->base;
|
||||
drm_atomic_private_obj_init(&kms->base, &layer->base.obj, &st->base.obj,
|
||||
&komeda_layer_obj_funcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_private_state *
|
||||
komeda_compiz_atomic_duplicate_state(struct drm_private_obj *obj)
|
||||
{
|
||||
struct komeda_compiz_state *st;
|
||||
|
||||
st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
komeda_component_state_reset(&st->base);
|
||||
__drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj);
|
||||
|
||||
return &st->base.obj;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_compiz_atomic_destroy_state(struct drm_private_obj *obj,
|
||||
struct drm_private_state *state)
|
||||
{
|
||||
kfree(to_compiz_st(priv_to_comp_st(state)));
|
||||
}
|
||||
|
||||
static const struct drm_private_state_funcs komeda_compiz_obj_funcs = {
|
||||
.atomic_duplicate_state = komeda_compiz_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_compiz_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static int komeda_compiz_obj_add(struct komeda_kms_dev *kms,
|
||||
struct komeda_compiz *compiz)
|
||||
{
|
||||
struct komeda_compiz_state *st;
|
||||
|
||||
st = kzalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->base.component = &compiz->base;
|
||||
drm_atomic_private_obj_init(&kms->base, &compiz->base.obj, &st->base.obj,
|
||||
&komeda_compiz_obj_funcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_private_state *
|
||||
komeda_improc_atomic_duplicate_state(struct drm_private_obj *obj)
|
||||
{
|
||||
struct komeda_improc_state *st;
|
||||
|
||||
st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
komeda_component_state_reset(&st->base);
|
||||
__drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj);
|
||||
|
||||
return &st->base.obj;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_improc_atomic_destroy_state(struct drm_private_obj *obj,
|
||||
struct drm_private_state *state)
|
||||
{
|
||||
kfree(to_improc_st(priv_to_comp_st(state)));
|
||||
}
|
||||
|
||||
static const struct drm_private_state_funcs komeda_improc_obj_funcs = {
|
||||
.atomic_duplicate_state = komeda_improc_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_improc_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static int komeda_improc_obj_add(struct komeda_kms_dev *kms,
|
||||
struct komeda_improc *improc)
|
||||
{
|
||||
struct komeda_improc_state *st;
|
||||
|
||||
st = kzalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->base.component = &improc->base;
|
||||
drm_atomic_private_obj_init(&kms->base, &improc->base.obj, &st->base.obj,
|
||||
&komeda_improc_obj_funcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_private_state *
|
||||
komeda_timing_ctrlr_atomic_duplicate_state(struct drm_private_obj *obj)
|
||||
{
|
||||
struct komeda_timing_ctrlr_state *st;
|
||||
|
||||
st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
komeda_component_state_reset(&st->base);
|
||||
__drm_atomic_helper_private_obj_duplicate_state(obj, &st->base.obj);
|
||||
|
||||
return &st->base.obj;
|
||||
}
|
||||
|
||||
static void
|
||||
komeda_timing_ctrlr_atomic_destroy_state(struct drm_private_obj *obj,
|
||||
struct drm_private_state *state)
|
||||
{
|
||||
kfree(to_ctrlr_st(priv_to_comp_st(state)));
|
||||
}
|
||||
|
||||
static const struct drm_private_state_funcs komeda_timing_ctrlr_obj_funcs = {
|
||||
.atomic_duplicate_state = komeda_timing_ctrlr_atomic_duplicate_state,
|
||||
.atomic_destroy_state = komeda_timing_ctrlr_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static int komeda_timing_ctrlr_obj_add(struct komeda_kms_dev *kms,
|
||||
struct komeda_timing_ctrlr *ctrlr)
|
||||
{
|
||||
struct komeda_compiz_state *st;
|
||||
|
||||
st = kzalloc(sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->base.component = &ctrlr->base;
|
||||
drm_atomic_private_obj_init(&kms->base, &ctrlr->base.obj, &st->base.obj,
|
||||
&komeda_timing_ctrlr_obj_funcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_private_state *
|
||||
komeda_pipeline_atomic_duplicate_state(struct drm_private_obj *obj)
|
||||
{
|
||||
|
@ -55,7 +237,7 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
|
|||
struct komeda_dev *mdev)
|
||||
{
|
||||
struct komeda_pipeline *pipe;
|
||||
int i, err;
|
||||
int i, j, err;
|
||||
|
||||
for (i = 0; i < mdev->n_pipelines; i++) {
|
||||
pipe = mdev->pipelines[i];
|
||||
|
@ -64,25 +246,33 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* Add component */
|
||||
for (j = 0; j < pipe->n_layers; j++) {
|
||||
err = komeda_layer_obj_add(kms, pipe->layers[j]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = komeda_compiz_obj_add(kms, pipe->compiz);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_improc_obj_add(kms, pipe->improc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = komeda_timing_ctrlr_obj_add(kms, pipe->ctrlr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev)
|
||||
void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms)
|
||||
{
|
||||
struct komeda_pipeline *pipe;
|
||||
struct komeda_component *c;
|
||||
int i, id;
|
||||
struct drm_mode_config *config = &kms->base.mode_config;
|
||||
struct drm_private_obj *obj, *next;
|
||||
|
||||
for (i = 0; i < mdev->n_pipelines; i++) {
|
||||
pipe = mdev->pipelines[i];
|
||||
dp_for_each_set_bit(id, pipe->avail_comps) {
|
||||
c = komeda_pipeline_get_component(pipe, id);
|
||||
|
||||
drm_atomic_private_obj_fini(&c->obj);
|
||||
}
|
||||
drm_atomic_private_obj_fini(&pipe->obj);
|
||||
}
|
||||
list_for_each_entry_safe(obj, next, &config->privobj_list, head)
|
||||
drm_atomic_private_obj_fini(obj);
|
||||
}
|
||||
|
|
|
@ -797,6 +797,50 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_old_private_obj_state
|
||||
* @state: global atomic state object
|
||||
* @obj: private_obj to grab
|
||||
*
|
||||
* This function returns the old private object state for the given private_obj,
|
||||
* or NULL if the private_obj is not part of the global atomic state.
|
||||
*/
|
||||
struct drm_private_state *
|
||||
drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state->num_private_objs; i++)
|
||||
if (obj == state->private_objs[i].ptr)
|
||||
return state->private_objs[i].old_state;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_new_private_obj_state
|
||||
* @state: global atomic state object
|
||||
* @obj: private_obj to grab
|
||||
*
|
||||
* This function returns the new private object state for the given private_obj,
|
||||
* or NULL if the private_obj is not part of the global atomic state.
|
||||
*/
|
||||
struct drm_private_state *
|
||||
drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state->num_private_objs; i++)
|
||||
if (obj == state->private_objs[i].ptr)
|
||||
return state->private_objs[i].new_state;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_connector_state - get connector state
|
||||
* @state: global atomic state object
|
||||
|
@ -1236,4 +1280,3 @@ int drm_atomic_debugfs_init(struct drm_minor *minor)
|
|||
minor->debugfs_root, minor);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -452,6 +452,12 @@ void drm_atomic_private_obj_fini(struct drm_private_obj *obj);
|
|||
struct drm_private_state * __must_check
|
||||
drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj);
|
||||
struct drm_private_state *
|
||||
drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj);
|
||||
struct drm_private_state *
|
||||
drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj);
|
||||
|
||||
/**
|
||||
* drm_atomic_get_existing_crtc_state - get crtc state, if it exists
|
||||
|
|
Loading…
Reference in New Issue