Merge remote-tracking branch 'drm/drm-next' into drm-misc-next
Requested for backmerging airlied's drm-legacy cleanup. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
This commit is contained in:
@@ -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_ */
|
||||
|
@@ -1,14 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ccflags-y := \
|
||||
-I$(src)/../include \
|
||||
-I$(src)
|
||||
-I $(srctree)/$(src)/../include \
|
||||
-I $(srctree)/$(src)
|
||||
|
||||
komeda-y := \
|
||||
komeda_drv.o \
|
||||
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_*/
|
||||
|
610
drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
Normal file
610
drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
Normal file
@@ -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);
|
||||
}
|
||||
|
مرجع در شماره جدید
Block a user