Hi Daniel,
Am Donnerstag, den 05.11.2015, 02:49 +0800 schrieb Daniel Kurtz:
Hi Philipp,
A bunch of review comments inline.
A bunch indeed. Thank you for the feedback.
On Wed, Nov 4, 2015 at 7:44 PM, Philipp Zabel p.zabel@pengutronix.de wrote:
[...]
+struct mtk_drm_crtc {
struct drm_crtc base;
unsigned int pipe;
There is one pipe too many :-) I think this one is not used."
bool enabled;
struct mtk_crtc_ddp_context *ctx;
I think you should be able to just embed the "mtk_crtc_ddp_context" right into mtk_drm_crtc. Or maybe just get rid of mtk_crtc_ddp_context completely and just use mtk_drm_crtc eveywhere.
I'll combine them and get rid of the superfluous pipe.
[...]
+static void mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *crtc) +{
[...]
/* disp_mtcmos */
ret = pm_runtime_get_sync(drm->dev);
if (ret < 0)
DRM_ERROR("Failed to enable power domain: %d\n", ret);
mtk_ddp_clock_on(ctx->mutex_dev);
mtk_crtc_ddp_power_on(ctx);
get_sync(), clock_on() and ddp_power_on() really can fail; we are just ignoring errors here. Since they can fail, shouldn't they be moved out of the atomic "->enable()" path into the ->check() path that is allowed to fail?
You suggest I move them into atomic_check?
To me it sounds like this should not be called from check, but from the drm_mode_config_funcs atomic_commit callback, right after calling drm_atomic_helper_prepare_planes. But there is no prepare equivalent to drm_atomic_helper_commit_modeset_enables.
DRM_INFO("mediatek_ddp_ddp_path_setup\n");
for (i = 0; i < (ctx->ddp_comp_nr - 1); i++) {
nit: the inner () are not necessary.
Will remove those.
mtk_ddp_add_comp_to_path(ctx->config_regs, ctx->pipe,
ctx->ddp_comp[i].funcs->comp_id,
ctx->ddp_comp[i+1].funcs->comp_id);
mtk_ddp_add_comp_to_mutex(ctx->mutex_dev, ctx->pipe,
ctx->ddp_comp[i].funcs->comp_id,
ctx->ddp_comp[i+1].funcs->comp_id);
}
Do you really have to do this here in the enable path? This looks like something that should be done in bind()?
Perhaps all we really need here is to walk the path and write to DISP_REG_MUTEX_EN at the end of mtk_ddp_add_comp_to_mutex(). By the way, where are those bits cleared in the disable path?
Disabling or changing the path is not implemented, the currently static setup could well be moved into bind().
[...]
+static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *crtc) +{
[...]
pm_runtime_put_sync(drm->dev);
Why sync?
I can think of no reason, will use the async version here.
[...]
+static void mtk_drm_crtc_disable(struct drm_crtc *crtc) +{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
DRM_INFO("mtk_drm_crtc_disable %d\n", crtc->base.id);
if (WARN_ON(!mtk_crtc->enabled))
return;
mtk_crtc_ddp_hw_fini(mtk_crtc);
mtk_crtc->do_flush = false;
if (state->event)
mtk_drm_crtc_finish_page_flip(mtk_crtc);
It is a bit awkward to send the page flip event before the actual page flip has completed (in this case, for a page flip that you are canceling by disabling the crtc). Would it be better to wait for vblank here in crtc_disable instead?
Yes, I will wait for vblank here instead.
[...]
+static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
+{
struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
if (state->base.event) {
state->base.event->pipe = drm_crtc_index(crtc);
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
state->event = state->base.event;
state->base.event = NULL;
Hmm. Why are we "stealing" event from drm_crtc_state and handing it over to the wrapper mtk_crtc_state? Won't we still be able to retrieve state->base.event later when we need it in the mtk_drm_crtc_finish_page_flip?
Hmm, good point. I'll try that.
[...]
+static void mtk_crtc_ddp_irq(struct mtk_crtc_ddp_context *ctx)
==> So, this is the part of the driver that makes me the most nervous. Why are we writing the actual hardware registers in the *vblank interrupt handler*?! Every other display controller that I've ever seen has shadow registers that are used to stage a page flip at the next vblank. Are you sure this hardware doesn't support this?
In any case, how do we get that first interrupt in which to apply the register?
I'll try to rework this using the DISP_MUTEX to trigger register updates on SOF.
+{
struct mtk_drm_crtc *mtk_crtc = ctx->crtc;
struct mtk_ddp_comp *ovl = &ctx->ddp_comp[0];
ddp_comp[0] here looks a bit like black magic. "The 0th component is always the ovl". Perhaps make an explicitly named "ovl" field for mtk_crtc_ddp_context.
Ok.
struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
unsigned int i;
if (state->pending_config) {
state->pending_config = false;
for (i = 0; i < OVL_LAYER_NR; i++)
ovl->funcs->comp_layer_off(ovl->regs, i);
Why do you need to turn off all layers before setting mode?
I am not sure if this is necessary. It seems to work without.
ovl->funcs->comp_config(ovl->regs, state->pending_width,
state->pending_height,
state->pending_vrefresh);
}
for (i = 0; i < OVL_LAYER_NR; i++) {
if (state->pending_ovl_config[i]) {
if (!state->pending_ovl_enable[i])
ovl->funcs->comp_layer_off(ovl->regs, i);
ovl->funcs->comp_layer_config(ovl->regs, i,
state->pending_ovl_addr[i],
state->pending_ovl_pitch[i],
state->pending_ovl_format[i],
state->pending_ovl_x[i],
state->pending_ovl_y[i],
state->pending_ovl_size[i]);
if (state->pending_ovl_enable[i])
ovl->funcs->comp_layer_on(ovl->regs, i);
}
I'd move this all all with an mtk_plane function, and let each mtk_plane store its own pending state.
Ok.
[...]
+static int mtk_crtc_ddp_bind(struct device *dev, struct device *master,
void *data)
+{
[...]
for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) {
type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY :
(zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
DRM_PLANE_TYPE_OVERLAY;
ret = mtk_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos, OVL_LAYER_NR);
if (ret)
goto unprepare;
}
I think you should just let mtk_drm_crtc_create() create & init its own planes. Currently, its overlay planes are unassigned. Furthermore, the crtc context doesn't really need an array of planes at all. It only really uses the array to know which planes to update during the vblank irq. But, that information is actually already present in the atomic state to be applied itself.
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h new file mode 100644 index 0000000..b696066 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h @@ -0,0 +1,56 @@
[...]
+struct mtk_crtc_state {
struct drm_crtc_state base;
struct drm_pending_vblank_event *event;
unsigned int pending_update;
bool pending_needs_vblank;
bool pending_config;
unsigned int pending_width;
unsigned int pending_height;
unsigned int pending_vrefresh;
bool pending_ovl_config[OVL_LAYER_NR];
bool pending_ovl_enable[OVL_LAYER_NR];
unsigned int pending_ovl_addr[OVL_LAYER_NR];
unsigned int pending_ovl_pitch[OVL_LAYER_NR];
unsigned int pending_ovl_format[OVL_LAYER_NR];
int pending_ovl_x[OVL_LAYER_NR];
int pending_ovl_y[OVL_LAYER_NR];
unsigned int pending_ovl_size[OVL_LAYER_NR];
bool pending_ovl_dirty[OVL_LAYER_NR];
All of these OVL_LAYER_NR length arrays look like mtk_plane_state to me. I think we want to do something similar and wrap drm_plane_state.
[...]
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c @@ -0,0 +1,218 @@
[...]
+#define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * n) +#define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * n) +#define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * n) +#define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * n)
The n should be in parens here.
Ok.
[...]
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs, unsigned int pipe,
enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next)
Why pass pipe if we don't use it?
Leftover from before the split of mtk_ddp_add_to_path/mutex.
[...]
if (value) {
unsigned int reg = readl(config_regs + addr) | value;
writel(reg, config_regs + addr);
Almost all of the writel() in this patch can really be writel_relaxed(). The only ones that need writel() are when you really need a barrier.
Ok.
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c new file mode 100644 index 0000000..2f3b32b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -0,0 +1,424 @@
[...]
+static void mtk_ovl_config(void __iomem *ovl_base,
unsigned int w, unsigned int h, unsigned int vrefresh)
+{
if (w != 0 && h != 0)
writel(h << 16 | w, ovl_base + DISP_REG_OVL_ROI_SIZE);
Should you just write 0 to DISP_REG_OVL_ROI_SIZE if w or h are 0?
According to the docs, width and height must be at least 1. But of course I should check this earlier.
[...]
+static unsigned int ovl_fmt_convert(unsigned int fmt)
return int so the error code stays negative?
I'll just return RGB888 on error.
[...]
+static void mtk_ovl_layer_config(void __iomem *ovl_base, unsigned int idx,
unsigned int addr, unsigned int pitch, unsigned int fmt,
int x, int y, unsigned int size)
+{
unsigned int reg;
reg = has_rb_swapped(fmt) << 24 | ovl_fmt_convert(fmt) << 12;
ovl_fmt_convert() can return < 0 for unsupported fmt. But, perhaps we can remove that check, since it should actually already be guaranteed by the drm core that a plane is only ever asked to display an FB with a format that the plane advertises.
Exactly. If something unsupported gets through here, it's a bug.
[...]
+static const struct mtk_ddp_comp_funcs ddp_color0 = {
.comp_id = DDP_COMPONENT_COLOR0,
.comp_power_on = mtk_color_start,
For consistency, can you name all of the "*_start()" functions "*_power_on", or change the .comp_power_on to .comp_start. Actually, just drop the "comp_" from all of the fields of mtk_ddp_comp_funcs, since it is redundant.
I'd change them all to .enable/.disable.
+static const struct mtk_ddp_comp_funcs ddp_ovl1 = {
.comp_id = DDP_COMPONENT_OVL1,
.comp_config = mtk_ovl_config,
.comp_power_on = mtk_ovl_start,
.comp_enable_vblank = mtk_ovl_enable_vblank,
.comp_disable_vblank = mtk_ovl_disable_vblank,
.comp_clear_vblank = mtk_ovl_clear_vblank,
.comp_layer_on = mtk_ovl_layer_on,
.comp_layer_off = mtk_ovl_layer_off,
.comp_layer_config = mtk_ovl_layer_config,
+};
The callback duplication here suggests the component model can be refined a bit. I think you probably want a single one of these:
static const struct mtk_ddp_comp_funcs ddp_ovl = { .config = mtk_ovl_config, .start = mtk_ovl_start, .enable_vblank = mtk_ovl_enable_vblank, .disable_vblank = mtk_ovl_disable_vblank, .clear_vblank = mtk_ovl_clear_vblank, .layer_on = mtk_ovl_layer_on, .layer_off = mtk_ovl_layer_off, .layer_config = mtk_ovl_layer_config, };
Yes, the next version will drop .comp_id from the funcs.
and then to use it for both ovl's. Maybe something like this:
static const struct mtk_ddp_comp_desc ddp_ovl0 = { .id = DDP_COMPONENT_OVL0, .ops = ddp_ovl };
static const struct mtk_ddp_comp_desc ddp_ovl1 = { .id = DDP_COMPONENT_OVL1, .ops = ddp_ovl };
These will not be necessary, I'll store the id in mtk_ddp_comp.
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h new file mode 100644 index 0000000..ae3a6e8 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h @@ -0,0 +1,86 @@
[...]
+static void mtk_atomic_complete(struct mtk_drm_private *private,
struct drm_atomic_state *state)
+{
struct drm_device *drm = private->drm;
drm_atomic_helper_commit_modeset_disables(drm, state);
drm_atomic_helper_commit_planes(drm, state);
drm_atomic_helper_commit_modeset_enables(drm, state);
drm_atomic_helper_wait_for_vblanks(drm, state);
Why wait for vblank here? Are you sure we waited long enough here? This will return after the very next vblank. However, what guarantees that the new state was really transferred to the hardware in time?
I'll check out how to use the DISP_MUTEX for this. It has an interrupt to signal when the register settings were applied.
[...]
+static int mtk_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *state,
bool async)
+{
struct mtk_drm_private *private = drm->dev_private;
int ret;
ret = drm_atomic_helper_prepare_planes(drm, state);
if (ret)
return ret;
mutex_lock(&private->commit.lock);
flush_work(&private->commit.work);
drm_atomic_helper_swap_state(drm, state);
if (async)
mtk_atomic_schedule(private, state);
else
mtk_atomic_complete(private, state);
mutex_unlock(&private->commit.lock);
What does this lock do? AFAICT, it only ensures that the second half of mtk_atomic_commit() cannot execute twice at the same time. However, I expect simultaneous calls to atomic_commit() should already be prohibited by the drm core?
I don't see it. DRM_IOCTL_MODE_ATOMIC is unlocked, and drm_mode_atomic_ioctl only calls drm_modeset_acquire_init but doesn't get any lock before calling drm_atomic_(async_)commit.
[...]
+static int mtk_drm_kms_init(struct drm_device *drm) +{
struct mtk_drm_private *private = drm->dev_private;
struct platform_device *pdev;
int ret;
int i;
pdev = of_find_device_by_node(private->mutex_node);
if (!pdev) {
dev_err(drm->dev, "Waiting for disp-mutex device %s\n",
private->mutex_node->full_name);
of_node_put(private->mutex_node);
return -EPROBE_DEFER;
}
private->mutex_dev = &pdev->dev;
for (i = 0; i < MAX_CRTC; i++) {
if (!private->larb_node[i])
break;
pdev = of_find_device_by_node(private->larb_node[i]);
if (!pdev) {
dev_err(drm->dev, "Waiting for larb device %s\n",
private->larb_node[i]->full_name);
Nit: dev_warn() perhaps?
Ok.
[...]
for (i = 0; i < MAX_CRTC; i++) {
if (!private->larb_dev[i])
break;
ret = mtk_smi_larb_get(private->larb_dev[i]);
Hmm. this looks like it should be done by a crtc function, and, only for CRTCs that are actually going to be used.
Yes, I suppose these should only be enabled while the respective DMA components (OVL, RDMA, WDMA) are active. I think the correct place for this would be in the component's .enable (or a new .prepare if necessary).
if (ret) {
DRM_ERROR("Failed to get larb: %d\n", ret);
goto err_larb_get;
}
}
drm_kms_helper_poll_init(drm);
drm_mode_config_reset(drm);
return 0;
+err_larb_get:
for (i = i - 1; i >= 0; i--)
mtk_smi_larb_put(private->larb_dev[i]);
drm_kms_helper_poll_fini(drm);
Not drm_kms_helper_poll_fini().
Ok.
[...]
+static void mtk_drm_kms_deinit(struct drm_device *drm) +{
put the larbs?
Ok, unless I move them elsewhere.
drm_kms_helper_poll_fini(drm);
drm_vblank_cleanup(drm);
unbind_all ?
Ok.
[...]
+static struct drm_driver mtk_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
| DRIVER_ATOMIC ?
Yes.
[...]
+static int mtk_drm_probe(struct platform_device *pdev) +{
[...]
/* Iterate over sibling DISP function blocks */
for_each_child_of_node(dev->of_node->parent, node) {
const struct of_device_id *of_id;
enum mtk_ddp_comp_type comp_type;
int comp_id;
of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
if (!of_id)
continue;
comp_type = (enum mtk_ddp_comp_type)of_id->data;
if (comp_type == MTK_DISP_MUTEX) {
private->mutex_node = of_node_get(node);
continue;
}
I'd move the MUTEX check after "is_available()", to catch the odd case where the MUTEX node is disabled (it will still be NULL, and fail below).
Ok.
if (!of_device_is_available(node)) {
dev_dbg(dev, "Skipping disabled component %s\n",
node->full_name);
continue;
}
comp_id = mtk_ddp_comp_get_id(node, comp_type);
if (comp_id < 0) {
dev_info(dev, "Skipping unknown component %s\n",
node->full_name);
dev_warn()
Ok.
[...]
if (comp_type == MTK_DISP_OVL) {
struct device_node *larb_node;
larb_node = of_parse_phandle(node, "mediatek,larb", 0);
if (larb_node && num_larbs < MAX_CRTC)
private->larb_node[num_larbs++] = larb_node;
It feels like the larb_nodes should be handled directly by the ovl driver.
Yes. And the rdma/wdma, probably. For that I'd like to split the ovl driver from the crtc implementation.
}
}
if (!private->mutex_node) {
dev_err(dev, "Failed to find disp-mutex node\n");
Cleanup: of_node_put() any nodes already in comp_node[] Anything to clean after component_match_add()? of_node_put() larb_nodes?
[...]
+static int mtk_drm_remove(struct platform_device *pdev) +{
component_master_del(&pdev->dev, &mtk_drm_ops);
pm_runtime_disable(&pdev->dev);
Cleanup: of_node_put() any nodes already in comp_node[]
Ok.
Anything to clean after component_match_add()?
No, that just calls devm_kmalloc internally.
of_node_put() larb_nodes?
Ok.
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h new file mode 100644 index 0000000..5e5128e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h @@ -0,0 +1,61 @@
[...]
+struct mtk_drm_private {
struct drm_fb_helper *fb_helper;
Not used?
Will drop, this belongs into a separate fb_helper patch (not part of this series).
struct drm_device *drm;
/*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
I don't understand this comment.
I'll just drop it. This array of pointers is needed to translate from pipe number to struct mtk_drm_crtc in crtc_enable/disable_vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
struct drm_property *plane_zpos_property;
Not used.
Will drop, this belongs into a separate zpos property patch (not part of this series).
unsigned int pipe;
what pipe is this?
That should be renamed to num_pipes.
struct device_node *larb_node[MAX_CRTC];
struct device *larb_dev[MAX_CRTC];
struct device_node *mutex_node;
struct device *mutex_dev;
void __iomem *config_regs;
unsigned int path_len[MAX_CRTC];
const enum mtk_ddp_comp_id *path[MAX_CRTC];
Having 5 arrays of length MAX_CRTC suggests you really want one to combine these fields into a struct, and have an MAX_CRTC array of the struct. In fact, this struct sounds like it should be "mtk_crtc", which wraps a drm_crtc.
The larb_node/dev should be moved into DMA component drivers, but a static path indeed currently maps directly to a crtc.
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c new file mode 100644 index 0000000..dfa931b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -0,0 +1,151 @@
[...]
+struct mtk_drm_fb {
struct drm_framebuffer base;
struct drm_gem_object *gem_obj[MAX_FB_OBJ];
Nit: The display controller driver does not handle multi-buffer formats. So, maybe just add the array later when it does. Arguably, for now it is just adding complexity with no benefit.
Ok.
[...]
+struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
struct drm_file *file,
struct drm_mode_fb_cmd2 *cmd)
+{
unsigned int hsub, vsub, i;
struct mtk_drm_fb *mtk_fb;
struct drm_gem_object *gem[MAX_FB_OBJ];
int err;
hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
nit: blank line here would help readability.
for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
Ok.
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h new file mode 100644 index 0000000..9ce7307 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h @@ -0,0 +1,29 @@ +struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
unsigned long size)
static? But actually, just folding this directly into mtk_drm_gem_create() would make the code flow clearer, since this function has no corresponding _fini().
Yes.
[...]
+struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
unsigned long size, bool alloc_kmap)
static?
It is referenced from mtk_drm_drv.c
[...]
+void mtk_drm_gem_free_object(struct drm_gem_object *obj) +{
struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
drm_gem_free_mmap_offset(obj);
/* release file pointer to gem object. */
drm_gem_object_release(obj);
drm_gem_object_release calls drm_gem_free_mmap_offset().
Ok.
dma_free_attrs(obj->dev->dev, obj->size, mtk_gem->cookie,
mtk_gem->dma_addr, &mtk_gem->dma_attrs);
I think you want to do this before calling drm_gem_object_release.
Why?
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h new file mode 100644 index 0000000..fb7953e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h @@ -0,0 +1,56 @@
[...]
+struct mtk_drm_gem_obj {
struct drm_gem_object base;
void __iomem *cookie;
void __iomem *kvaddr;
Is kvaddr really __iomem?
No, and neither is cookie.
[...]
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c new file mode 100644 index 0000000..3a8843c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -0,0 +1,167 @@
[...]
+static void mtk_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
+{
struct drm_plane_state *state = plane->state;
struct drm_gem_object *gem_obj;
struct drm_crtc *crtc = state->crtc;
struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
struct drm_rect dest = {
.x1 = state->crtc_x,
.y1 = state->crtc_y,
.x2 = state->crtc_x + state->crtc_w,
.y2 = state->crtc_y + state->crtc_h,
};
struct drm_rect clip = { 0, };
if (!crtc)
return;
clip.x2 = state->crtc->state->mode.hdisplay;
clip.y2 = state->crtc->state->mode.vdisplay;
drm_rect_intersect(&dest, &clip);
mtk_plane->disp_size = (dest.y2 - dest.y1) << 16 | (dest.x2 - dest.x1);
plane->fb = state->fb;
This doesn't look right. The drm core should be doing this for us. Why do you need this here?
I really think we want to be using a "struct mtk_plane_state" which wraps a drm_plane_state and accumulates our ovl specific changes. During init, when creating crtcs & planes, we should tell each plane its id, and which ovl it should use. The mtk_plane code can then provide a function for the vblank irq to apply mtk_plane's pending state.
I agree.
gem_obj = mtk_fb_get_gem_obj(state->fb, 0);
mtk_plane->flip_obj = to_mtk_gem_obj(gem_obj);
We do not need to store "flip_obj". We only use it twice, +3 and +5 lines down from here. Also, this doesn't look safe. What if state->fb is NULL?
Since we implement atomic_disable, atomic_update should never be called with state->fb == NULL.
It works because to_mtk_gem_obj(NULL) == NULL, but it relies on an internal implementation detail - gem_obj.base is at offset 0.
mtk_plane->crtc = crtc;
This isn't used, either.
Will drop.
[...]
+static void mtk_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
+{
struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
struct drm_crtc *crtc = old_state->crtc;
if (!crtc)
return;
mtk_drm_crtc_plane_config(crtc, mtk_plane->idx, false, 0);
mtk_drm_crtc_commit(crtc);
Why this here? Won't this happen during mtk_drm_crtc_atomic_flush?
Because that just marks a configuration change in pending_ovl_config, which you already suggested rightfully belongs in mtk_plane_state. I'll fix that.
[...]
+int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
unsigned int zpos, unsigned int max_plane)
max_plane is not used.
Ok... enough for today :-).
This is also part of the zpos property patch, I'll drop it.
regards Philipp