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:
Maarten Lankhorst
2019-05-09 10:18:57 +02:00
کامیت 752c4f3c1d
1397فایلهای تغییر یافته به همراه23254 افزوده شده و 16129 حذف شده

مشاهده پرونده

@@ -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_*/

مشاهده پرونده

@@ -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);
}