/* * If cancel_delayed_work returns false * the timeout expired. The watchdog is running, * and will take care of finishing the job.
*/ if (cancel_delayed_work(&vpu->watchdog_work)) { if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done)
ctx->codec_ops->done(ctx);
hantro_job_finish(vpu, ctx, result);
}
}
/* * Driver does mostly sequential access, so sacrifice TLB efficiency * for faster allocation. Also, no CPU access on the source queue, * so no kernel mapping needed.
*/
src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
DMA_ATTR_NO_KERNEL_MAPPING;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->vpu_mutex;
src_vq->dev = ctx->dev->v4l2_dev.dev;
src_vq->supports_requests = true;
ret = vb2_queue_init(src_vq); if (ret) return ret;
dst_vq->bidirectional = true;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; /* * The Kernel needs access to the JPEG destination buffer for the * JPEG encoder to fill in the JPEG headers.
*/ if (!ctx->is_encoder) {
dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
dst_vq->max_num_buffers = MAX_POSTPROC_BUFFERS;
}
/* * We do not need any extra locking here, because we operate only * on local data here, except reading few fields from dev, which * do not change through device's lifetime (which is guaranteed by * reference on module from open()) and V4L2 internal objects (such * as vdev and ctx->fh), which have proper locking done in respective * helper functions used here.
*/
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM;
ret = hantro_ctrls_setup(vpu, ctx, allowed_codecs); if (ret) {
vpu_err("Failed to set up controls\n"); goto err_fh_free;
}
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
/* * No need for extra locking because this was the last reference * to this file.
*/
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
kfree(ctx);
/* Create the three encoder entities with their pads */
func->source_pad.flags = MEDIA_PAD_FL_SOURCE;
ret = hantro_register_entity(mdev, &func->vdev.entity, "source",
&func->source_pad, 1, MEDIA_ENT_F_IO_V4L,
&func->vdev); if (ret) return ret;
func->proc_pads[0].flags = MEDIA_PAD_FL_SINK;
func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE;
ret = hantro_register_entity(mdev, &func->proc, "proc",
func->proc_pads, 2, func->id,
&func->vdev); if (ret) goto err_rel_entity0;
func->sink_pad.flags = MEDIA_PAD_FL_SINK;
ret = hantro_register_entity(mdev, &func->sink, "sink",
&func->sink_pad, 1, MEDIA_ENT_F_IO_V4L,
&func->vdev); if (ret) goto err_rel_entity1;
/* Connect the three entities */
ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 0,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED); if (ret) goto err_rel_entity2;
ret = media_create_pad_link(&func->proc, 1, &func->sink, 0,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED); if (ret) goto err_rm_links0;
/* Create video interface */
func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO,
0, VIDEO_MAJOR,
func->vdev.minor); if (!func->intf_devnode) {
ret = -ENOMEM; goto err_rm_links1;
}
/* Connect the two DMA engines to the interface */
link = media_create_intf_link(&func->vdev.entity,
&func->intf_devnode->intf,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED); if (!link) {
ret = -ENOMEM; goto err_rm_devnode;
}
link = media_create_intf_link(&func->sink, &func->intf_devnode->intf,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED); if (!link) {
ret = -ENOMEM; goto err_rm_devnode;
} return 0;
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) {
v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); return ret;
}
ret = hantro_attach_func(vpu, func); if (ret) {
v4l2_err(&vpu->v4l2_dev, "Failed to attach functionality to the media device\n"); goto err_unreg_dev;
}
v4l2_info(&vpu->v4l2_dev, "registered %s as /dev/video%d\n", vfd->name,
vfd->num);
/* * Some SoCs, like RK3588 have multiple identical Hantro cores, but the * kernel is currently missing support for multi-core handling. Exposing * separate devices for each core to userspace is bad, since that does * not allow scheduling tasks properly (and creates ABI). With this workaround * the driver will only probe for the first core and early exit for the other * cores. Once the driver gains multi-core support, the same technique * for detecting the main core can be used to cluster all cores together.
*/ staticint hantro_disable_multicore(struct hantro_dev *vpu)
{ struct device_node *node = NULL; constchar *compatible; bool is_main_core; int ret;
/* Intentionally ignores the fallback strings */
ret = of_property_read_string(vpu->dev->of_node, "compatible", &compatible); if (ret) return ret;
/* The first compatible and available node found is considered the main core */ do {
node = of_find_compatible_node(node, NULL, compatible); if (of_device_is_available(node)) break;
} while (node);
if (!node) return -EINVAL;
is_main_core = (vpu->dev->of_node == node);
of_node_put(node);
if (!is_main_core) {
dev_info(vpu->dev, "missing multi-core support, ignoring this instance\n"); return -ENODEV;
}
return 0;
}
staticint hantro_probe(struct platform_device *pdev)
{ conststruct of_device_id *match; struct hantro_dev *vpu; int num_bases; int i, ret;
vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); if (!vpu) return -ENOMEM;
match = of_match_node(of_hantro_match, pdev->dev.of_node);
vpu->variant = match->data;
ret = hantro_disable_multicore(vpu); if (ret) return ret;
/* * Support for nxp,imx8mq-vpu is kept for backwards compatibility * but it's deprecated. Please update your DTS file to use * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead.
*/ if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu"))
dev_warn(&pdev->dev, "%s compatible is deprecated\n",
match->compatible);
vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks, sizeof(*vpu->clocks), GFP_KERNEL); if (!vpu->clocks) return -ENOMEM;
if (vpu->variant->num_clocks > 1) { for (i = 0; i < vpu->variant->num_clocks; i++)
vpu->clocks[i].id = vpu->variant->clk_names[i];
ret = devm_clk_bulk_get(&pdev->dev, vpu->variant->num_clocks,
vpu->clocks); if (ret) return ret;
} else { /* * If the driver has a single clk, chances are there will be no * actual name in the DT bindings.
*/
vpu->clocks[0].clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(vpu->clocks[0].clk)) return PTR_ERR(vpu->clocks[0].clk);
}
vpu->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev); if (IS_ERR(vpu->resets)) return PTR_ERR(vpu->resets);
for (i = 0; i < num_bases; i++) {
vpu->reg_bases[i] = vpu->variant->reg_names ?
devm_platform_ioremap_resource_byname(pdev, vpu->variant->reg_names[i]) :
devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vpu->reg_bases[i])) return PTR_ERR(vpu->reg_bases[i]);
}
vpu->enc_base = vpu->reg_bases[0] + vpu->variant->enc_offset;
vpu->dec_base = vpu->reg_bases[0] + vpu->variant->dec_offset;
/** * TODO: Eventually allow taking advantage of full 64-bit address space. * Until then we assume the MSB portion of buffers' base addresses is * always 0 due to this masking operation.
*/
ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); if (ret) {
dev_err(vpu->dev, "Could not set DMA coherent mask.\n"); return ret;
}
vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
for (i = 0; i < vpu->variant->num_irqs; i++) { constchar *irq_name; int irq;
if (!vpu->variant->irqs[i].handler) continue;
if (vpu->variant->num_irqs > 1) {
irq_name = vpu->variant->irqs[i].name;
irq = platform_get_irq_byname(vpu->pdev, irq_name);
} else { /* * If the driver has a single IRQ, chances are there * will be no actual name in the DT bindings.
*/
irq_name = "default";
irq = platform_get_irq(vpu->pdev, 0);
} if (irq < 0) return irq;
ret = devm_request_irq(vpu->dev, irq,
vpu->variant->irqs[i].handler, 0,
dev_name(vpu->dev), vpu); if (ret) {
dev_err(vpu->dev, "Could not request %s IRQ.\n",
irq_name); return ret;
}
}
if (vpu->variant->init) {
ret = vpu->variant->init(vpu); if (ret) {
dev_err(&pdev->dev, "Failed to init VPU hardware\n"); return ret;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.