/* destroy a plane */ static void xilinx_drm_plane_destroy(struct drm_plane *base_plane) { struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane); unsigned int i; xilinx_drm_plane_dpms(base_plane, DRM_MODE_DPMS_OFF); plane->manager->planes[plane->id] = NULL; drm_plane_cleanup(base_plane); for (i = 0; i < MAX_NUM_SUB_PLANES; i++) if (plane->dma[i].chan) dma_release_channel(plane->dma[i].chan); if (plane->manager->osd) { xilinx_osd_layer_disable(plane->osd_layer); xilinx_osd_layer_put(plane->osd_layer); } if (plane->manager->dp_sub) { xilinx_drm_dp_sub_layer_disable(plane->manager->dp_sub, plane->dp_layer); xilinx_drm_dp_sub_layer_put(plane->manager->dp_sub, plane->dp_layer); } }
/* destroy a plane */ static void xilinx_drm_plane_destroy(struct drm_plane *base_plane) { struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane); xilinx_drm_plane_dpms(base_plane, DRM_MODE_DPMS_OFF); plane->manager->planes[plane->id] = NULL; drm_plane_cleanup(base_plane); dma_release_channel(plane->dma.chan); if (plane->manager->osd) { xilinx_osd_layer_disable(plane->osd_layer); xilinx_osd_layer_put(plane->osd_layer); } }
/* create a plane */ static struct xilinx_drm_plane * xilinx_drm_plane_create(struct xilinx_drm_plane_manager *manager, unsigned int possible_crtcs, bool primary) { struct xilinx_drm_plane *plane; struct device *dev = manager->drm->dev; char name[16]; struct device_node *plane_node; struct device_node *sub_node; enum drm_plane_type type; uint32_t fmt_in = -1; uint32_t fmt_out = -1; const char *fmt; int i; int ret; for (i = 0; i < manager->num_planes; i++) if (!manager->planes[i]) break; if (i >= manager->num_planes) { DRM_ERROR("failed to allocate plane\n"); return ERR_PTR(-ENODEV); } snprintf(name, sizeof(name), "plane%d", i); plane_node = of_get_child_by_name(manager->node, name); if (!plane_node) { DRM_ERROR("failed to find a plane node\n"); return ERR_PTR(-ENODEV); } plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL); if (!plane) { ret = -ENOMEM; goto err_out; } plane->primary = primary; plane->id = i; plane->prio = i; plane->zpos = i; plane->alpha = manager->default_alpha; plane->dpms = DRM_MODE_DPMS_OFF; plane->format = -1; DRM_DEBUG_KMS("plane->id: %d\n", plane->id); for (i = 0; i < MAX_NUM_SUB_PLANES; i++) { snprintf(name, sizeof(name), "dma%d", i); plane->dma[i].chan = of_dma_request_slave_channel(plane_node, name); if (PTR_ERR(plane->dma[i].chan) == -ENODEV) { plane->dma[i].chan = NULL; continue; } if (IS_ERR(plane->dma[i].chan)) { DRM_ERROR("failed to request dma channel\n"); ret = PTR_ERR(plane->dma[i].chan); plane->dma[i].chan = NULL; goto err_dma; } } /* probe color space converter */ sub_node = of_parse_phandle(plane_node, "xlnx,rgb2yuv", i); if (sub_node) { plane->rgb2yuv = xilinx_rgb2yuv_probe(dev, sub_node); of_node_put(sub_node); if (IS_ERR(plane->rgb2yuv)) { DRM_ERROR("failed to probe a rgb2yuv\n"); ret = PTR_ERR(plane->rgb2yuv); goto err_dma; } /* rgb2yuv input format */ plane->format = DRM_FORMAT_XRGB8888; /* rgb2yuv output format */ fmt_out = DRM_FORMAT_YUV444; } /* probe chroma resampler */ sub_node = of_parse_phandle(plane_node, "xlnx,cresample", i); if (sub_node) { plane->cresample = xilinx_cresample_probe(dev, sub_node); of_node_put(sub_node); if (IS_ERR(plane->cresample)) { DRM_ERROR("failed to probe a cresample\n"); ret = PTR_ERR(plane->cresample); goto err_dma; } /* cresample input format */ fmt = xilinx_cresample_get_input_format_name(plane->cresample); ret = xilinx_drm_format_by_name(fmt, &fmt_in); if (ret) goto err_dma; /* format sanity check */ if ((fmt_out != -1) && (fmt_out != fmt_in)) { DRM_ERROR("input/output format mismatch\n"); ret = -EINVAL; goto err_dma; } if (plane->format == -1) plane->format = fmt_in; /* cresample output format */ fmt = xilinx_cresample_get_output_format_name(plane->cresample); ret = xilinx_drm_format_by_name(fmt, &fmt_out); if (ret) goto err_dma; } /* create an OSD layer when OSD is available */ if (manager->osd) { /* format sanity check */ if ((fmt_out != -1) && (fmt_out != manager->format)) { DRM_ERROR("input/output format mismatch\n"); ret = -EINVAL; goto err_dma; } /* create an osd layer */ plane->osd_layer = xilinx_osd_layer_get(manager->osd); if (IS_ERR(plane->osd_layer)) { DRM_ERROR("failed to create a osd layer\n"); ret = PTR_ERR(plane->osd_layer); plane->osd_layer = NULL; goto err_dma; } if (plane->format == -1) plane->format = manager->format; } if (manager->dp_sub) { plane->dp_layer = xilinx_drm_dp_sub_layer_get(manager->dp_sub, primary); if (IS_ERR(plane->dp_layer)) { DRM_ERROR("failed to create a dp_sub layer\n"); ret = PTR_ERR(plane->dp_layer); plane->dp_layer = NULL; goto err_dma; } if (primary) { ret = xilinx_drm_dp_sub_layer_set_fmt(manager->dp_sub, plane->dp_layer, manager->format); if (ret) { DRM_ERROR("failed to set dp_sub layer fmt\n"); goto err_dma; } } plane->format = xilinx_drm_dp_sub_layer_get_fmt(manager->dp_sub, plane->dp_layer); } /* If there's no IP other than VDMA, pick the manager's format */ if (plane->format == -1) plane->format = manager->format; /* initialize drm plane */ type = primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(manager->drm, &plane->base, possible_crtcs, &xilinx_drm_plane_funcs, &plane->format, 1, type, NULL); if (ret) { DRM_ERROR("failed to initialize plane\n"); goto err_init; } plane->manager = manager; manager->planes[plane->id] = plane; xilinx_drm_plane_attach_property(&plane->base); of_node_put(plane_node); return plane; err_init: if (manager->dp_sub) { xilinx_drm_dp_sub_layer_disable(manager->dp_sub, plane->dp_layer); xilinx_drm_dp_sub_layer_put(plane->manager->dp_sub, plane->dp_layer); } if (manager->osd) { xilinx_osd_layer_disable(plane->osd_layer); xilinx_osd_layer_put(plane->osd_layer); } err_dma: for (i = 0; i < MAX_NUM_SUB_PLANES; i++) if (plane->dma[i].chan) dma_release_channel(plane->dma[i].chan); err_out: of_node_put(plane_node); return ERR_PTR(ret); }
/* set plane dpms */ void xilinx_drm_plane_dpms(struct drm_plane *base_plane, int dpms) { struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane); struct xilinx_drm_plane_manager *manager = plane->manager; unsigned int i; DRM_DEBUG_KMS("plane->id: %d\n", plane->id); DRM_DEBUG_KMS("dpms: %d -> %d\n", plane->dpms, dpms); if (plane->dpms == dpms) return; plane->dpms = dpms; switch (dpms) { case DRM_MODE_DPMS_ON: if (manager->dp_sub) { if (plane->primary) { xilinx_drm_dp_sub_enable_alpha(manager->dp_sub, plane->alpha_enable); xilinx_drm_dp_sub_set_alpha(manager->dp_sub, plane->alpha); } xilinx_drm_dp_sub_layer_enable(manager->dp_sub, plane->dp_layer); } /* start dma engine */ for (i = 0; i < MAX_NUM_SUB_PLANES; i++) if (plane->dma[i].chan && plane->dma[i].is_active) dma_async_issue_pending(plane->dma[i].chan); if (plane->rgb2yuv) xilinx_rgb2yuv_enable(plane->rgb2yuv); if (plane->cresample) xilinx_cresample_enable(plane->cresample); /* enable osd */ if (manager->osd) { xilinx_osd_disable_rue(manager->osd); xilinx_osd_layer_set_priority(plane->osd_layer, plane->prio); xilinx_osd_layer_enable_alpha(plane->osd_layer, plane->alpha_enable); xilinx_osd_layer_set_alpha(plane->osd_layer, plane->alpha); xilinx_osd_layer_enable(plane->osd_layer); xilinx_osd_enable_rue(manager->osd); } break; default: /* disable/reset osd */ if (manager->osd) { xilinx_osd_disable_rue(manager->osd); xilinx_osd_layer_set_dimension(plane->osd_layer, 0, 0, 0, 0); xilinx_osd_layer_disable(plane->osd_layer); xilinx_osd_enable_rue(manager->osd); } if (plane->cresample) { xilinx_cresample_disable(plane->cresample); xilinx_cresample_reset(plane->cresample); } if (plane->rgb2yuv) { xilinx_rgb2yuv_disable(plane->rgb2yuv); xilinx_rgb2yuv_reset(plane->rgb2yuv); } /* stop dma engine and release descriptors */ for (i = 0; i < MAX_NUM_SUB_PLANES; i++) { if (plane->dma[i].chan && plane->dma[i].is_active) { dmaengine_terminate_all(plane->dma[i].chan); plane->dma[i].is_active = false; } } if (manager->dp_sub) xilinx_drm_dp_sub_layer_disable(manager->dp_sub, plane->dp_layer); break; } }
/* set plane dpms */ void xilinx_drm_plane_dpms(struct drm_plane *base_plane, int dpms) { struct xilinx_drm_plane *plane = to_xilinx_plane(base_plane); struct xilinx_drm_plane_manager *manager = plane->manager; DRM_DEBUG_KMS("plane->id: %d\n", plane->id); DRM_DEBUG_KMS("dpms: %d -> %d\n", plane->dpms, dpms); if (plane->dpms == dpms) return; plane->dpms = dpms; switch (dpms) { case DRM_MODE_DPMS_ON: /* start dma engine */ dma_async_issue_pending(plane->dma.chan); if (plane->rgb2yuv) xilinx_rgb2yuv_enable(plane->rgb2yuv); if (plane->cresample) xilinx_cresample_enable(plane->cresample); /* enable osd */ if (manager->osd) { xilinx_osd_disable_rue(manager->osd); xilinx_osd_layer_set_priority(plane->osd_layer, plane->prio); xilinx_osd_layer_set_alpha(plane->osd_layer, 1, plane->alpha); xilinx_osd_layer_enable(plane->osd_layer); if (plane->priv) { /* set background color as black */ xilinx_osd_set_color(manager->osd, 0x0, 0x0, 0x0); xilinx_osd_enable(manager->osd); } xilinx_osd_enable_rue(manager->osd); } break; default: /* disable/reset osd */ if (manager->osd) { xilinx_osd_disable_rue(manager->osd); xilinx_osd_layer_set_dimension(plane->osd_layer, 0, 0, 0, 0); xilinx_osd_layer_disable(plane->osd_layer); if (plane->priv) xilinx_osd_reset(manager->osd); xilinx_osd_enable_rue(manager->osd); } if (plane->cresample) { xilinx_cresample_disable(plane->cresample); xilinx_cresample_reset(plane->cresample); } if (plane->rgb2yuv) { xilinx_rgb2yuv_disable(plane->rgb2yuv); xilinx_rgb2yuv_reset(plane->rgb2yuv); } /* stop dma engine and release descriptors */ dmaengine_terminate_all(plane->dma.chan); break; } }