diff --git a/arch/arm64/boot/dts/apple/t600x-die0.dtsi b/arch/arm64/boot/dts/apple/t600x-die0.dtsi index 6bf9802ed4dc8b..637e052e5db3c3 100644 --- a/arch/arm64/boot/dts/apple/t600x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-die0.dtsi @@ -109,7 +109,6 @@ label = "pmp-avd-sys"; #power-domain-cells = <0>; power-domains = <&ps_avd_sys>; - status = "disabled"; }; pmp_report_msr0: report@12 { diff --git a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi index 121d158ff1b3ea..e222d63b37d320 100644 --- a/arch/arm64/boot/dts/apple/t600x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t600x-dieX.dtsi @@ -24,6 +24,34 @@ #performance-domain-cells = <0>; }; + DIE_NODE(avd): avd@286000000 { + compatible = "apple,t6000-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + #ifdef APPLE_USE_PMP + power-domains = <&DIE_NODE(pmp_report_avd_sys)>; + #else + power-domains = <&DIE_NODE(ps_avd_sys)>; + #endif + resets = <&DIE_NODE(ps_avd_sys)>; + reg = <0x2 0x87080000 0x0 0x10000>, + <0x2 0x87090000 0x0 0x10000>, + <0x2 0x870a0000 0x0 0x4000>, + <0x2 0x87100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&DIE_NODE(avd_dart) 0>; + }; + + DIE_NODE(avd_dart): iommu@287010000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x87010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_avd_sys)>; + }; + DIE_NODE(dispext0_dart): iommu@289304000 { compatible = "apple,t6000-dart"; reg = <0x2 0x89304000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t602x-die0.dtsi b/arch/arm64/boot/dts/apple/t602x-die0.dtsi index c0c0626249a5a6..8938dd90693c4d 100644 --- a/arch/arm64/boot/dts/apple/t602x-die0.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-die0.dtsi @@ -115,7 +115,6 @@ label = "pmp-avd-sys"; #power-domain-cells = <0>; power-domains = <&ps_avd_sys>; - status = "disabled"; }; pmp_report_msr0: report@12 { diff --git a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi index ae0038a4c28710..d916c0b106d109 100644 --- a/arch/arm64/boot/dts/apple/t602x-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-dieX.dtsi @@ -23,6 +23,34 @@ #performance-domain-cells = <0>; }; + DIE_NODE(avd): avd@286000000 { + compatible = "apple,t6020-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + #ifdef APPLE_USE_PMP + power-domains = <&DIE_NODE(pmp_report_avd_sys)>; + #else + power-domains = <&DIE_NODE(ps_avd_sys)>; + #endif + resets = <&DIE_NODE(ps_avd_sys)>; + reg = <0x2 0x87080000 0x0 0x10000>, + <0x2 0x87090000 0x0 0x10000>, + <0x2 0x870a0000 0x0 0x4000>, + <0x2 0x87100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&DIE_NODE(avd_dart) 0>; + }; + + DIE_NODE(avd_dart): iommu@287010000 { + compatible = "apple,t6020-dart", "apple,t8110-dart"; + reg = <0x2 0x87010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_avd_sys)>; + }; + DIE_NODE(dispext0_dart): iommu@289304000 { compatible = "apple,t6020-dart", "apple,t8110-dart"; reg = <0x2 0x89304000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t6030.dtsi b/arch/arm64/boot/dts/apple/t6030.dtsi index 6918f022faab8a..8912a7e771b964 100644 --- a/arch/arm64/boot/dts/apple/t6030.dtsi +++ b/arch/arm64/boot/dts/apple/t6030.dtsi @@ -642,6 +642,30 @@ status = "disabled"; }; + avd: avd@30a000000 { + compatible = "apple,t6030-avd", "apple,t8122-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + power-domains = <&ps_avd_sys>; + resets = <&ps_avd_sys>; + reg = <0x3 0x0b080000 0x0 0x12000>, + <0x3 0x0b092000 0x0 0x12000>, + <0x3 0x0b0a4000 0x0 0x4000>, + <0x3 0x0b100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&avd_dart 0>; + }; + + avd_dart: iommu@30b010000 { + compatible = "apple,t6030-dart", "apple,t8110-dart"; + reg = <0x3 0x0b010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_avd_sys>; + }; + pinctrl_ap: pinctrl@347100000 { compatible = "apple,t6030-pinctrl", "apple,t8103-pinctrl"; reg = <0x3 0x47100000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t6031-dieX.dtsi b/arch/arm64/boot/dts/apple/t6031-dieX.dtsi index 159448916a5dbb..d4e4afd1bc96c7 100644 --- a/arch/arm64/boot/dts/apple/t6031-dieX.dtsi +++ b/arch/arm64/boot/dts/apple/t6031-dieX.dtsi @@ -91,6 +91,30 @@ ; }; + DIE_NODE(avd): avd@2cc000000 { + compatible = "apple,t6031-avd", "apple,t8122-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + power-domains = <&DIE_NODE(ps_avd_sys)>; + resets = <&DIE_NODE(ps_avd_sys)>; + reg = <0x2 0xcd080000 0x0 0x12000>, + <0x2 0xcd092000 0x0 0x12000>, + <0x2 0xcd0a4000 0x0 0x4000>, + <0x2 0xcd100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&DIE_NODE(avd_dart) 0>; + }; + + DIE_NODE(avd_dart): iommu@2cd010000 { + compatible = "apple,t6031-dart", "apple,t8110-dart"; + reg = <0x2 0xcd010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&DIE_NODE(ps_avd_sys)>; + }; + DIE_NODE(sio_dart): iommu@391004000 { compatible = "apple,t6031-dart", "apple,t8110-dart"; reg = <0x3 0x91004000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi index d3fc50b8f901b5..dfb5f737a420b3 100644 --- a/arch/arm64/boot/dts/apple/t8103.dtsi +++ b/arch/arm64/boot/dts/apple/t8103.dtsi @@ -1406,6 +1406,30 @@ }; }; + avd: avd@268000000 { + compatible = "apple,t8103-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + power-domains = <&ps_avd_sys>; + resets = <&ps_avd_sys>; + reg = <0x2 0x69080000 0x0 0xc000>, + <0x2 0x6908c000 0x0 0xc000>, + <0x2 0x69098000 0x0 0x4000>, + <0x2 0x69100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&avd_dart 0>; + }; + + avd_dart: iommu@269010000 { + compatible = "apple,t8103-dart"; + reg = <0x2 0x69010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_avd_sys>; + }; + dispext0_dart: iommu@271304000 { compatible = "apple,t8103-dart"; reg = <0x2 0x71304000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t8112.dtsi b/arch/arm64/boot/dts/apple/t8112.dtsi index b667944f6dc5c3..b19b94305e47c1 100644 --- a/arch/arm64/boot/dts/apple/t8112.dtsi +++ b/arch/arm64/boot/dts/apple/t8112.dtsi @@ -1155,7 +1155,6 @@ label = "pmp-avd-sys"; #power-domain-cells = <0>; power-domains = <&ps_avd_sys>; - status = "disabled"; }; pmp_report_msr: report@b { @@ -1665,6 +1664,34 @@ #mbox-cells = <0>; }; + avd: avd@268000000 { + compatible = "apple,t8112-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + #ifdef APPLE_USE_PMP + power-domains = <&pmp_report_avd_sys>; + #else + power-domains = <&ps_avd_sys>; + #endif + resets = <&ps_avd_sys>; + reg = <0x2 0x69080000 0x0 0x10000>, + <0x2 0x69090000 0x0 0x10000>, + <0x2 0x690a0000 0x0 0x4000>, + <0x2 0x69100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&avd_dart 0>; + }; + + avd_dart: iommu@269010000 { + compatible = "apple,t8110-dart"; + reg = <0x2 0x69010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_avd_sys>; + }; + dispext0_dart: iommu@271304000 { compatible = "apple,t8112-dart", "apple,t8110-dart"; reg = <0x2 0x71304000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/apple/t8122.dtsi b/arch/arm64/boot/dts/apple/t8122.dtsi index c1c109bb0a8425..50b39bfe521952 100644 --- a/arch/arm64/boot/dts/apple/t8122.dtsi +++ b/arch/arm64/boot/dts/apple/t8122.dtsi @@ -465,6 +465,30 @@ power-domains = <&ps_sio_cpu>; }; + avd: avd@288000000 { + compatible = "apple,t8122-avd"; + interrupt-parent = <&aic>; + interrupts = , + ; + power-domains = <&ps_avd_sys>; + resets = <&ps_avd_sys>; + reg = <0x2 0x89080000 0x0 0x12000>, + <0x2 0x89092000 0x0 0x12000>, + <0x2 0x890a4000 0x0 0x4000>, + <0x2 0x89100000 0x0 0x10000>; + reg-names = "code", "sram", "mbox", "ctrl"; + iommus = <&avd_dart 0>; + }; + + avd_dart: iommu@289010000 { + compatible = "apple,t8122-dart", "apple,t8110-dart"; + reg = <0x2 0x89010000 0x0 0x4000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_avd_sys>; + }; + fpwm1: pwm@2a1044000 { compatible = "apple,t8122-fpwm", "apple,s5l-fpwm"; reg = <0x2 0xa1044000 0x0 0x4000>; diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 2b05e570c421ea..a51832662985f6 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -662,6 +662,8 @@ apple_dart_setup_translation(struct apple_dart_domain *domain, struct io_pgtable_cfg *pgtbl_cfg = &io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg; + apple_dart_hw_reset(stream_map->dart); + for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i) { u64 ttbr = virt_to_phys(pgtbl_cfg->apple_dart_cfg.ttbr[i]); apple_dart_hw_set_ttbr(stream_map, i, ttbr); diff --git a/drivers/media/platform/apple/Kconfig b/drivers/media/platform/apple/Kconfig index f16508bff5242a..77588c8f4e5a58 100644 --- a/drivers/media/platform/apple/Kconfig +++ b/drivers/media/platform/apple/Kconfig @@ -3,3 +3,4 @@ comment "Apple media platform drivers" source "drivers/media/platform/apple/isp/Kconfig" +source "drivers/media/platform/apple/avd/Kconfig" diff --git a/drivers/media/platform/apple/Makefile b/drivers/media/platform/apple/Makefile index d8fe985b0e6c37..72f8bb54b376df 100644 --- a/drivers/media/platform/apple/Makefile +++ b/drivers/media/platform/apple/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += isp/ +obj-y += avd/ diff --git a/drivers/media/platform/apple/avd/Kconfig b/drivers/media/platform/apple/avd/Kconfig new file mode 100644 index 00000000000000..43ec5fa52d4cd1 --- /dev/null +++ b/drivers/media/platform/apple/avd/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_APPLE_AVD + tristate "Apple Silicon Video Decoding driver" + depends on VIDEO_DEV + depends on MEDIA_CONTROLLER + depends on ARCH_APPLE || COMPILE_TEST + depends on OF_ADDRESS + depends on V4L_PLATFORM_DRIVERS + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select V4L2_H264 + select V4L2_VP9 diff --git a/drivers/media/platform/apple/avd/Makefile b/drivers/media/platform/apple/avd/Makefile new file mode 100644 index 00000000000000..54ea67a589cdfd --- /dev/null +++ b/drivers/media/platform/apple/avd/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +apple-avd-y := avd-drv.o avd-v4l2.o avd-hw.o avd-h264.o avd-vp9.o +obj-$(CONFIG_VIDEO_APPLE_AVD) += apple-avd.o diff --git a/drivers/media/platform/apple/avd/avd-drv.c b/drivers/media/platform/apple/avd/avd-drv.c new file mode 100644 index 00000000000000..773de35ce883a0 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-drv.c @@ -0,0 +1,712 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple Video Decoder driver + * + * Copyright The Asahi Linux Contributors + * + * Based on rkvdec driver by Collabora, Ltd. + * Copyright (C) 2019 Collabora, Ltd. + * Based on rkvdec driver by Google LLC. (Tomasz Figa ) + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include +#include + +#include "avd.h" +#include "avd-regs.h" + +void fill_rvra(struct avd_rvra *rvra, enum avd_image_fmt image_fmt, + u32 width, u32 height) +{ + u32 size0, size1, size2; + u32 hs = round_up(height, 32); + + size0 = (width * hs) + ((width * hs) / 4); + size2 = size0; + + switch (image_fmt) { + case AVD_IMG_FMT_420_8BIT: + case AVD_IMG_FMT_420_10BIT: + size2 /= 2; + break; + default: + break; + } + + size1 = max((roundup_pow_of_two(width) * roundup_pow_of_two(height)) / 32, + 0x100u); + /* TODO: how big? */ + + rvra->size = round_up(size0 + size1 + size2, 0x4000); + rvra->size += + (width < 1000 ? 0 : width < 1800 ? 2 : width < 3800 ? 3 : 9) * 0x4000; + + rvra->offsets[1] = 0; + rvra->offsets[0] = size0; + rvra->offsets[3] = size0 + size1; + rvra->offsets[2] = size0 + size1 + size2; +} + + +int alloc_slots(struct avd_dev *avd, struct avd_ctx *ctx, enum avd_codec codec) { + u32 free; + u32 offset = 0; + for (int i = 0; i < codec; i++) + offset += avd->variant->vp_slots[i]; + + free = find_next_zero_bit(&avd->vp_slots, + offset + avd->variant->vp_slots[codec], + offset); + + if (free >= offset + avd->variant->vp_slots[codec]) + return -ENOMEM; + + set_bit(free, &avd->vp_slots); + ctx->vp_slot = free; + + ctx->fifo_idx = find_first_zero_bit(&avd->inst_fifo_slots, + avd->variant->fifo_slots); + + if (WARN_ON(ctx->fifo_idx >= avd->variant->fifo_slots)) { + clear_bit(free, &avd->vp_slots); + return -ENOMEM; + } + set_bit(ctx->fifo_idx, &avd->inst_fifo_slots); + + return 0; +} + +int avd_buf_alloc(struct avd_dev *avd, struct avd_buf *buf, size_t size) +{ + buf->size = size; + buf->cpu = + dma_alloc_coherent(avd->dev, buf->size, &buf->addr, GFP_KERNEL); + return buf->cpu ? 0 : -ENOMEM; +} + +void avd_buf_free(struct avd_dev *avd, struct avd_buf *buf) +{ + if (buf->cpu) + dma_free_coherent(avd->dev, buf->size, buf->cpu, buf->addr); + memset(buf, 0, sizeof(*buf)); +} + +static int avd_reset(struct avd_dev *avd) +{ + int ret = 0; + avd->vp_slots = 0; + avd->inst_fifo_slots = 0; + + ret = pm_runtime_resume_and_get(avd->dev); + if (ret < 0) + return ret; + + ret = reset_control_reset(avd->rstc); + if (ret) + dev_err(avd->dev, "reset: failed: %d", ret); + + if (avd->empty_domain) { + iommu_attach_device(avd->empty_domain, avd->dev); + iommu_detach_device(avd->empty_domain, avd->dev); + } + + ret = avd_boot(avd); + if (ret) + dev_err(avd->dev, "reset: failed to boot"); + + pm_runtime_put_autosuspend(avd->dev); + + return ret; +} + +static void avd_watchdog_func(struct work_struct *work) +{ + struct avd_dev *avd; + struct avd_ctx *ctx; + int ret; + ctx = container_of(to_delayed_work(work), struct avd_ctx, + watchdog_work); + if (!ctx) + return; + + avd = ctx->dev; + + dev_err(avd->dev, "Frame processing timed out! Vp: %d (%02d)", + ctx->vp_slot, ctx->fifo_idx); + + free_vp_slot(avd, ctx); + free_inst_slot(avd, ctx); + + writel(0, avd->mbox + AVD_REG_MBOX_IRQ_ENABLE); + ret = avd_reset(avd); + if (ret) + dev_err(avd->dev, "failed to reset: %d", ret); + + avd_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static irqreturn_t avd_irq_handler(int irq, void *data) +{ + struct avd_dev *avd = data; + struct avd_ctx *ctx = v4l2_m2m_get_curr_priv(avd->m2m_dev); + + enum vb2_buffer_state state; + u32 status; + if (!ctx) + return IRQ_HANDLED; + + status = readl(avd->mbox + AVD_REG_MBOX1_RETRIEVE); + + writel(AVD_MBOX1_NOT_EMPTY, avd->mbox + AVD_REG_MBOX_IRQ_CLR); + + if (status & 0x10000) { /* dbg */ + dev_warn(avd->dev, "no handler for IRQ: %3d", status &~0x10000); + writel_relaxed(0, avd->mbox + AVD_REG_MBOX_IRQ_ENABLE); + return IRQ_HANDLED; + } + + if (status & 0x1000) { + /* pp is done ! we are done */ + state = VB2_BUF_STATE_DONE; + + free_inst_slot(avd, ctx); + } else if (status & 0x100) { + free_vp_slot(avd, ctx); + /* a vp is done, kick the pp and hope for the best */ + if(ctx->coded_fmt_desc->ops->submit) + ctx->coded_fmt_desc->ops->submit(ctx); + + goto done; + } else { + dev_err(avd->dev, "H%d %02d error", status, ctx->fifo_idx); + /* let watchdog handle */ + goto done; + } + + /* if the watchdog_work has run the work has already been submitted */ + if (cancel_delayed_work(&ctx->watchdog_work)) + avd_job_finish(ctx, state); + +done: + return IRQ_HANDLED; +} + +static void avd_device_run(void *priv) +{ + struct avd_ctx *ctx = priv; + struct avd_dev *avd = ctx->dev; + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + int ret; + + if (WARN_ON(!desc)) + return; + + ret = pm_runtime_resume_and_get(avd->dev); + if (ret < 0) { + avd_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR); + return; + } + + ret = desc->ops->run(ctx); + if (ret) + avd_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static int avd_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct avd_ctx *ctx = priv; + int ret; + + /* TODO: what flags to give to dart */ + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->ops = &avd_queue_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + src_vq->dma_attrs = 0 /* 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->vdev_lock; + 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_NO_KERNEL_MAPPING; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->ops = &avd_queue_ops; + dst_vq->buf_struct_size = sizeof(struct avd_decoded_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->vdev_lock; + dst_vq->dev = ctx->dev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static int avd_open(struct file *filp) +{ + struct avd_dev *avd = video_drvdata(filp); + struct avd_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = avd; + + INIT_DELAYED_WORK(&ctx->watchdog_work, avd_watchdog_func); + + avd_reset_coded_fmt(ctx); + avd_reset_decoded_fmt(ctx); + + v4l2_fh_init(&ctx->fh, video_devdata(filp)); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(avd->m2m_dev, ctx, avd_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_free_ctx; + } + + ret = avd_init_ctrls(ctx); + if (ret) + goto err_cleanup_m2m_ctx; + + v4l2_fh_add(&ctx->fh, filp); + + return 0; + +err_cleanup_m2m_ctx: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + +err_free_ctx: + kfree(ctx); + return ret; +} + +static int avd_release(struct file *filp) +{ + struct avd_ctx *ctx = file_to_ctx(filp); + + v4l2_fh_del(&ctx->fh, filp); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations avd_fops = { + .owner = THIS_MODULE, + .open = avd_open, + .release = avd_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct v4l2_m2m_ops avd_m2m_ops = { + .device_run = avd_device_run, +}; + +static const struct media_device_ops avd_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static int avd_v4l2_init(struct avd_dev *avd) +{ + int ret; + + ret = v4l2_device_register(avd->dev, &avd->v4l2_dev); + if (ret) { + dev_err(avd->dev, "Failed to register V4L2 device\n"); + return ret; + } + + avd->m2m_dev = v4l2_m2m_init(&avd_m2m_ops); + if (IS_ERR(avd->m2m_dev)) { + v4l2_err(&avd->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(avd->m2m_dev); + goto err_unregister_v4l2; + } + + avd->mdev.dev = avd->dev; + strscpy(avd->mdev.model, "avd", sizeof(avd->mdev.model)); + strscpy(avd->mdev.bus_info, "platform:avd", sizeof(avd->mdev.bus_info)); + media_device_init(&avd->mdev); + avd->mdev.ops = &avd_media_ops; + avd->v4l2_dev.mdev = &avd->mdev; + + avd->vdev.lock = &avd->vdev_lock; + avd->vdev.v4l2_dev = &avd->v4l2_dev; + avd->vdev.fops = &avd_fops; + avd->vdev.release = video_device_release_empty; + avd->vdev.vfl_dir = VFL_DIR_M2M; + avd->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + avd->vdev.ioctl_ops = &avd_ioctl_ops; + video_set_drvdata(&avd->vdev, avd); + strscpy(avd->vdev.name, "avd", sizeof(avd->vdev.name)); + + ret = video_register_device(&avd->vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&avd->v4l2_dev, "Failed to register video device\n"); + goto err_cleanup_mc; + } + + ret = v4l2_m2m_register_media_controller( + avd->m2m_dev, &avd->vdev, MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&avd->v4l2_dev, + "Failed to initialize V4L2 M2M media controller\n"); + goto err_unregister_vdev; + } + + ret = media_device_register(&avd->mdev); + if (ret) { + v4l2_err(&avd->v4l2_dev, "Failed to register media device\n"); + goto err_unregister_mc; + } + + return 0; + +err_unregister_mc: + v4l2_m2m_unregister_media_controller(avd->m2m_dev); + +err_unregister_vdev: + video_unregister_device(&avd->vdev); + +err_cleanup_mc: + media_device_cleanup(&avd->mdev); + v4l2_m2m_release(avd->m2m_dev); + +err_unregister_v4l2: + v4l2_device_unregister(&avd->v4l2_dev); + return ret; +} + +static void avd_v4l2_cleanup(struct avd_dev *avd) +{ + media_device_unregister(&avd->mdev); + v4l2_m2m_unregister_media_controller(avd->m2m_dev); + video_unregister_device(&avd->vdev); + media_device_cleanup(&avd->mdev); + v4l2_m2m_release(avd->m2m_dev); + v4l2_device_unregister(&avd->v4l2_dev); +} + +static const struct avd_variant avd_t8103_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 2, /* no sure */ + [AVD_CODEC_H264] = 1, + [AVD_CODEC_VP9] = 1, + }, + .fifo_slots = 7, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9, + .configure_stream = t8103_configure_stream, + .fw_name = "apple/avd-fw-v2-t0.bin", + .revision = 3, + .quirks = AVD_QUIRK_LSR | AVD_QUIRK_NO_PIPE_STATE, + .vp_slot_offset = 0x4004, + .submit_offset = 0x4014, +}; + +static const struct avd_variant avd_t6000_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 4, + [AVD_CODEC_H264] = 4, + [AVD_CODEC_VP9] = 1, + }, + .fifo_slots = 15, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9, + .configure_stream = t8112_configure_stream, + .fw_name = "apple/avd-fw-v3-t0.bin", + .revision = 4, + .quirks = AVD_QUIRK_LSR | AVD_QUIRK_NO_PIPE_STATE, + .vp_slot_offset = 0xc, + .submit_offset = 0x30, +}; + +static const struct avd_variant avd_t8112_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 4, + [AVD_CODEC_H264] = 4, + [AVD_CODEC_VP9] = 1, + }, + .fifo_slots = 15, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9, + .configure_stream = t8112_configure_stream, + .fw_name = "apple/avd-fw-v3-t1.bin", + .revision = 4, + .quirks = AVD_QUIRK_LSR, + .vp_slot_offset = 0xc, + .submit_offset = 0x30, +}; + +static const struct avd_variant avd_t6020_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 4, + [AVD_CODEC_H264] = 4, + [AVD_CODEC_VP9] = 1, + }, + .fifo_slots = 15, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9, + .configure_stream = t8112_configure_stream, + .fw_name = "apple/avd-fw-v3-t1.bin", + .revision = 4, + .vp_slot_offset = 0xc, + .submit_offset = 0x30, +}; + +static const struct avd_variant avd_t8122_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 4, + [AVD_CODEC_H264] = 4, + [AVD_CODEC_VP9] = 1, + [AVD_CODEC_AV1] = 2, + }, + .fifo_slots = 15, + .configure_stream = t8122_configure_stream, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9 | + AVD_CODEC_AV1, + .fw_name = "apple/avd-fw-v4-t0.bin", + .revision = 4, + .vp_slot_offset = 0xc, + .submit_offset = 0x40, +}; + +static const struct avd_variant avd_t8140_variant = { + /* This is filled in using the tunables, what vp/pp to use? */ + .vp_slots = { + [AVD_CODEC_HEVC] = 2, + [AVD_CODEC_H264] = 1, + [AVD_CODEC_VP9] = 1, + [AVD_CODEC_AV1] = 1, + }, + .fifo_slots = 7, + .configure_stream = t8122_configure_stream, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9 | + AVD_CODEC_AV1, + .fw_name = "apple/avd-fw-v5-t0.bin", + .revision = 4, + .vp_slot_offset = 0xc, + .submit_offset = 0x40, +}; + +static const struct avd_variant avd_t8132_variant = { + .vp_slots = { + [AVD_CODEC_HEVC] = 4, + [AVD_CODEC_H264] = 4, + [AVD_CODEC_VP9] = 1, + [AVD_CODEC_AV1] = 3, + }, + .fifo_slots = 15, + .configure_stream = t8122_configure_stream, + .capabilities = AVD_CAPABILITY_HEVC | + AVD_CAPABILITY_H264 | + AVD_CAPABILITY_VP9 | + AVD_CODEC_AV1, + .fw_name = "apple/avd-fw-v5-t1.bin", + .revision = 4, + .vp_slot_offset = 0xc, + .submit_offset = 0x40, +}; + +/* can also be derived from a version register */ +static const struct of_device_id avd_of_match[] = { + { + .compatible = "apple,t8103-avd", + .data = &avd_t8103_variant + }, + { + .compatible = "apple,t6000-avd", + .data = &avd_t6000_variant + }, + { + .compatible = "apple,t8112-avd", + .data = &avd_t8112_variant + }, + { + .compatible = "apple,t6020-avd", + .data = &avd_t6020_variant + }, + { + .compatible = "apple,t8122-avd", + .data = &avd_t8122_variant + }, + { + .compatible = "apple,t8132-avd", + .data = &avd_t8132_variant + }, + { + .compatible = "apple,t8140-avd", + .data = &avd_t8140_variant + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, avd_of_match); + +static int avd_probe(struct platform_device *pdev) +{ + struct avd_dev *avd; + const struct of_device_id *match; + int ret, irq; + + avd = devm_kzalloc(&pdev->dev, sizeof(*avd), GFP_KERNEL); + if (!avd) + return -ENOMEM; + + platform_set_drvdata(pdev, avd); + avd->dev = &pdev->dev; + avd->pdev = pdev; + + mutex_init(&avd->vdev_lock); + + match = of_match_node(avd_of_match, pdev->dev.of_node); + avd->variant = match->data; + + avd->rstc = devm_reset_control_get_exclusive(avd->dev, NULL); + + avd->code = devm_platform_ioremap_resource_byname(pdev, "code"); + if (IS_ERR(avd->code)) + return PTR_ERR(avd->code); + avd->mbox = devm_platform_ioremap_resource_byname(pdev, "mbox"); + if (IS_ERR(avd->mbox)) + return PTR_ERR(avd->mbox); + avd->ctrl = devm_platform_ioremap_resource_byname(pdev, "ctrl"); + if (IS_ERR(avd->ctrl)) + return PTR_ERR(avd->ctrl); + + avd->domain = iommu_get_domain_for_dev(avd->dev); + if (avd->domain) { + avd->empty_domain = iommu_paging_domain_alloc(avd->dev); + if (IS_ERR(avd->empty_domain)) { + avd->empty_domain = NULL; + dev_warn(avd->dev, "cannot alloc new empty domain"); + } + } + + ret = request_firmware(&avd->fw, avd->variant->fw_name, avd->dev); + + if (ret) { + dev_err(avd->dev, "failed to load firmware: %d", ret); + return ret; + } + + ret = dma_set_mask_and_coherent(avd->dev, + DMA_BIT_MASK((avd->variant->quirks & AVD_QUIRK_LSR) ? 38 : 64)); + + if (ret) { + dev_err(avd->dev, "Failed to set DMA mask"); + return ret; + } + + irq = platform_get_irq(pdev, 1); + if (irq < 0) + return irq; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + avd_irq_handler, IRQF_ONESHOT, + dev_name(&pdev->dev), avd); + if (ret) { + dev_err(avd->dev, "Could not request IRQ 1"); + return ret; + } + + pm_runtime_set_autosuspend_delay(avd->dev, 100); + pm_runtime_use_autosuspend(avd->dev); + pm_runtime_enable(avd->dev); + + ret = avd_v4l2_init(avd); + if (ret) + goto err_disable_runtime_pm; + + return 0; + +err_disable_runtime_pm: + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +const struct avd_coded_fmt_ops avd_hevc_fmt_ops; + +static void avd_remove(struct platform_device *pdev) +{ + struct avd_dev *avd = platform_get_drvdata(pdev); + + release_firmware(avd->fw); + + avd_v4l2_cleanup(avd); + + if (avd->empty_domain) + iommu_domain_free(avd->empty_domain); + + pm_runtime_disable(avd->dev); + pm_runtime_dont_use_autosuspend(avd->dev); +} + +static int avd_runtime_resume(struct device *dev) +{ + int ret; + struct avd_dev *avd = platform_get_drvdata(to_platform_device(dev)); + + ret = avd_boot(avd); + if (ret) + dev_err(dev, "failed to boot"); + return ret; +} + +static int avd_runtime_suspend(struct device *dev) +{ + struct avd_dev *avd = platform_get_drvdata(to_platform_device(dev)); + + avd_shutdown(avd); + return 0; +} + +static const struct dev_pm_ops avd_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS( + pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(avd_runtime_suspend, + avd_runtime_resume, NULL) }; + +static struct platform_driver avd_driver = { + .probe = avd_probe, + .remove = avd_remove, + .driver = { + .name = "avd", + .of_match_table = avd_of_match, + .pm = &avd_pm_ops, + }, +}; +module_platform_driver(avd_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Apple avd v4l2 sl m2m"); diff --git a/drivers/media/platform/apple/avd/avd-h264.c b/drivers/media/platform/apple/avd/avd-h264.c new file mode 100644 index 00000000000000..a3cfc5dd34e5d6 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-h264.c @@ -0,0 +1,784 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple AVD VPU codec driver + * + * Copyright The Asahi Linux Contributors + * Copyright 2023 Eileen Yoon + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * Herman Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + */ + +#include "linux/dev_printk.h" +#include +#include + +#include +#include + +#include "avd.h" +#include "avd-inst.h" + +struct avd_h264_run { + struct avd_run base; + + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_slice_params *slice_params; + + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + const struct v4l2_ctrl_h264_pred_weights *pred_weights; + + struct run_addr { + dma_addr_t y; + dma_addr_t uv; + dma_addr_t sl; + dma_addr_t rvra; + dma_addr_t sps; + } addresses; + + s32 cur_poc; + u8 num_valid; +}; + +/* state */ +struct avd_h264_ctx { + struct avd_h264_reflists { + struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; + } reflists; + + struct avd_h264_bufs { + struct avd_buf pps_tile[5]; + struct avd_buf inst; + struct avd_buf pipe_state; + } bufs; +}; + +/* ffmpeg submits wrong timestamp, use first_mb_in_slice as a workaround */ +#define is_new_frame(sl) (sl->first_mb_in_slice == 0) /* (ctx->fh.m2m_ctx->new_frame) */ + + +/* scaling matrix */ +static const u32 default_8x8_intra[] = { + 0x060a0d10, 0x0a0b1012, 0x0d101217, 0x10121719, 0x1217191b, 0x17191b1d, + 0x191b1d1f, 0x1b1d1f21, 0x1217191b, 0x17191b1d, 0x191b1d1f, 0x1b1d1f21, + 0x1d1f2124, 0x1f212426, 0x21242628, 0x2426282a, +}; +static const u32 default_8x8_inter[] = { + 0x090d0f11, 0x0d0d1113, 0x0f111315, 0x11131516, 0x13151618, 0x15161819, + 0x1618191b, 0x18191b1c, 0x13151618, 0x15161819, 0x1618191b, 0x18191b1c, + 0x191b1c1e, 0x1b1c1e20, 0x1c1e2021, 0x1e202123, +}; + + +static inline u32 sps_size(u32 w, u32 h) +{ + /* TODO: does it really need so much? */ + return (((w - 1) * (h - 1) / 0x10000) + 2) * 0x4000; +} + +/* sorry for the formatting */ + +/* clang-format off */ +static void stream_refs(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = decode->dpb; + struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_dev *avd = ctx->dev; + + push(INST_DMA2, "cm3_dma_config_6"); + pusha(h264_ctx->bufs.pps_tile[4].addr, "hdr_9c_pps_tile_addr_lsb8", 7); + pusha(run->addresses.sps, "hdr_bc_sps_tile_addr_lsb8", 0); + + push(INST_DMA3, "cm3_dma_config_7"); + push(INST_DMA3, "cm3_dma_config_8"); + push(INST_DMA3, "cm3_dma_config_9"); + push(INST_DMA3, "cm3_dma_config_a"); + + for (int i = 0; i < ARRAY_SIZE(decode->dpb); i++) { + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) + continue; + + struct vb2_buffer *vb = vb2_find_buffer( + &ctx->fh.m2m_ctx->cap_q_ctx.q, dpb[i].reference_ts); + + dma_addr_t rvra_addr = vb + ? vb2_dma_contig_plane_dma_addr(vb, 0) + + (vb->planes[0].length + - sps_size(fmt_width(ctx), fmt_height(ctx)) + - ctx->rvra.size) + : run->addresses.rvra; /* safe fallback */ + + push(((run->num_valid - 1) & 0xf) << 28 + | 0x1000000 + | (boolify(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) << 17) + | (swrap(run->cur_poc - dpb[i].top_field_order_cnt, 1 << 17)), + "hdr_d0_ref_hdr"); + + push_rvra(avd, ctx, rvra_addr, ctx->rvra.offsets); + } +} + +static void stream_scaling(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; + struct avd_dev *avd = ctx->dev; + + push(0x1000000 | ((64 / 4) << 5) + | (((16 / 4) << 5) - 1), "hdr_4c_pic_scaling_list_dims"); + + for (int i = 0; i < 6; i++) + for (int j = 0; j < 16; j+=4) + push((scaling->scaling_list_4x4[i][j + 0] << 24) + | (scaling->scaling_list_4x4[i][j + 1] << 16) + | (scaling->scaling_list_4x4[i][j + 2] << 8) + | (scaling->scaling_list_4x4[i][j + 3]), + "scl_46c_pic_scaling_matrix_4x4"); + + /* Instead of 8x8 raster scan order avd expects 4 4x4 subblocks */ + static const u8 map[16] = { + 0, 8, 16, 24, /* top left */ + 4, 12, 20, 28, /* top right */ + 32, 40, 48, 56, /* bottom left */ + 36, 44, 52, 60, /* bottom right */ + }; + + /* 7.3.2.2, only matrix 0 and 1 are used if chroma_format_idc < 3 */ + if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) { + for (int i = 0; i < 2; i++) + for (int j = 0; j < 16; j++) + push((scaling->scaling_list_8x8[i][map[j] + 0] << 24) + | (scaling->scaling_list_8x8[i][map[j] + 1] << 16) + | (scaling->scaling_list_8x8[i][map[j] + 2] << 8) + | (scaling->scaling_list_8x8[i][map[j] + 3]), + "scl_4cc_pic_scaling_matrix_8x8"); + + } else { + for (int i = 0; i < ARRAY_SIZE(default_8x8_intra); i++) + push(default_8x8_intra[i], "scl_4cc_pic_scaling_matrix_8x8"); + for (int i = 0; i < ARRAY_SIZE(default_8x8_inter); i++) + push(default_8x8_inter[i], "scl_4cc_pic_scaling_matrix_8x8"); + } +} + +static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + struct avd_dev *avd = ctx->dev; + struct avd_h264_ctx *h264_ctx = ctx->priv; + u32 bytesperline; + u32 width = (sps->pic_width_in_mbs_minus1 + 1) * 16; + u32 height = (sps->pic_height_in_map_units_minus1 + 1) * 16; + + push(0x2b000000 + | (ctx->fifo_idx << 4) + | (avd->variant->revision == 3 ? 0x100 : 0x200), + "inst_fifo_start"); + + push(0x2db00000 + | 0x1000 + | ((decode->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) ? 0x2000 : 0) + | 0x2e0 + | (avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE ? 0 : 0x80000) + , "hdr_34_start_hdr"); + + push(AVD_CODEC_H264 << 24, "hdr_38_mode"); + + push(((height - 1) << 16) | (width - 1), "hdr_3c_height_width"); + + push(0, "hdr_40_zero"); + + push((((height - 1) >> 3) << 16) | ((width - 1) >> 3), + "hdr_28_height_width_shift3"); + + push((sps->chroma_format_idc & 3) << 24 + | (sps->bit_depth_luma_minus8 & 15) << 19 + | (sps->bit_depth_chroma_minus8 & 15) << 15 + | 0x2800 + | boolify(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) << 7 + | boolify(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), + "hdr_2c_sps_param"); + + push(boolify(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) << 20 + | !boolify(decode->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) << 21 + | boolify(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) << 19, + "hdr_44_flags"); + + + push(swrap(pps->chroma_qp_index_offset, 32) << 5 + | swrap(pps->second_chroma_qp_index_offset, 32) + , "hdr_48_chroma_qp_index_offset"); + + push(0x30000a + | (avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE ? 0 : 0x30), + "hdr_58_const_3a"); + + push(INST_DMA2, "cm3_dma_config_1"); + push(INST_DMA1, "cm3_dma_config_2"); + + if (avd->variant->revision == 3) + push(0, "zero"); + + pusha(h264_ctx->bufs.pps_tile[0].addr, "hdr_9c_pps_tile_addr_lsb8", 0); + + push(INST_DMA2, "cm3_dma_config_3"); + push(INST_DMA2, "cm3_dma_config_4"); + + if (avd->variant->revision == 3) + push(0, "zero"); + else if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) + pusha(h264_ctx->bufs.pipe_state.addr, "pipe_state", 0); + + pusha(h264_ctx->bufs.pps_tile[1].addr, "hdr_9c_pps_tile_addr_lsb8", 1); + pusha(h264_ctx->bufs.pps_tile[2].addr, "hdr_9c_pps_tile_addr_lsb8", 2); + pusha(h264_ctx->bufs.pps_tile[3].addr, "hdr_9c_pps_tile_addr_lsb8", 3); + push(INST_DMA3, "cm3_dma_config_5"); + + push_rvra(avd, ctx, run->addresses.rvra, ctx->rvra.offsets); + + bytesperline = ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline; + if (avd->variant->quirks & AVD_QUIRK_LSR) + bytesperline = bytesperline >> 4; + + pusha(run->addresses.y, "hdr_210_y_addr_lsb8", 0); + push(bytesperline, "hdr_218_width_align"); + pusha(run->addresses.uv,"hdr_214_uv_addr_lsb8", 0); + push(bytesperline, "hdr_21c_width_align"); + + push(0x0, "cm3_mark_end_section"); + push(((height - 1) << 16) | (width - 1), "hdr_54_height_width"); + + if (!(decode->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC)) + stream_refs(ctx, run); + + if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) + stream_scaling(ctx, run); + else + push(0x0, "cm3_mark_end_section_scl"); +} + +static void stream_weights(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + int default_luma_weight, default_chroma_weight; + struct v4l2_h264_weight_factors factors; + const struct v4l2_ctrl_h264_pred_weights *weights = run->pred_weights; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_slice_params *sl = run->slice_params; + struct avd_dev *avd = ctx->dev; + + bool pred_weight = V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(pps, sl); + + /* TODO: is there a better flag or something for the + * pps->weighted_bipred_idc == 2 checks? */ + push(0x2dd00000 + | ((pps->weighted_bipred_idc == 2) << 7) + | (pred_weight << 6) + | (weights->luma_log2_weight_denom << 3) + | (weights->chroma_log2_weight_denom) + /* default luma and chroma denom */ + | (pps->weighted_bipred_idc == 2 ? 0x5 | 0x5 << 3 : 0), + "slc_76c_cmd_weights_denom"); + + if (!pred_weight) + return; + + default_luma_weight = 1 << weights->luma_log2_weight_denom; + default_chroma_weight = 1 << weights->chroma_log2_weight_denom; + + + for (int y = 0; y < 2; y++) { + if (y == 1 && sl->slice_type != V4L2_H264_SLICE_TYPE_B) + break; + + factors = weights->weight_factors[y]; + int to = y == 0 ? sl->num_ref_idx_l0_active_minus1 + : sl->num_ref_idx_l1_active_minus1; + for (int i = 0; i < to + 1; i++) { + /* Avd only expects offsets/weights if they are not the default + * ones, otherwise we get artifacts */ + if((factors.luma_weight[i] != default_luma_weight) + || (factors.luma_offset[i] != 0)) { + push(0x2de00000 + | 1 << 14 + | y << 13 + | i << 9 + | ((factors.luma_weight[i] & 0x1ff)), + "slc_luma_weights"); + push(0x2df00000 + | swrap(factors.luma_offset[i], 0x10000), + "slc_luma_offsets"); + } + + if ((factors.chroma_weight[i][0] != default_chroma_weight) + || (factors.chroma_offset[i][0] != 0) + || (factors.chroma_weight[i][1] != default_chroma_weight) + || (factors.chroma_offset[i][1] != 0)) { + push(0x2de00000 + | 2 << 14 + | y << 13 + | i << 9 + | (factors.chroma_weight[i][0] & 0x1ff), + "slc_chroma_weights[0]"); + push(0x2df00000 + | swrap(factors.chroma_offset[i][0], 0x10000), + "slc_chroma_offsets[0]"); + push(0x2de00000 + | 3 << 14 + | y << 13 + | i << 9 + | (factors.chroma_weight[i][1] & 0x1ff), + "slc_chroma_weights[1]"); + push(0x2df00000 + | swrap(factors.chroma_offset[i][1], 0x10000), + "slc_chroma_offsets[1]"); + } + } + } +} + +static u32 stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_slice_params *sl = run->slice_params; + struct avd_dev *avd = ctx->dev; + struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + u32 payload_len = vb2_get_plane_payload(&src->vb2_buf, 0); + bool en_mode = (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) == + 0; + const u8 *data = vb2_plane_vaddr(&src->vb2_buf, 0); + u32 x; + + u32 min_off = (sl->header_bit_size + (en_mode ? 0 : 7)) / 8; + + /* emulation byte. header_bit_size does not include this. + * I wonder if its worth it to just not support this. */ + u32 off = 2; + u32 bytes_read = 2; + + while (bytes_read < min_off) { + if (data[off - 2] != 0x00 || data[off - 1] != 0x00 || + data[off] != 0x03) + bytes_read++; + off++; + } + + + dma_addr_t slc_a84 = run->addresses.sl + off; + + push(0x2d800000 + | ((en_mode ? (sl->header_bit_size % 8) : 0) << 15) + | (u32)(slc_a84 >> 32), "slc_a7c_cmd_set_coded_slice"); + push((u32)(slc_a84 & 0xffffffff), "slc_a84_slice_addr_low"); + + /* should not include trailing 0? */ + push(payload_len - off, "slc_a88_slice_hdr_size"); + + push(0x2c000000 + | (sl->first_mb_in_slice / (sps->pic_width_in_mbs_minus1 + 1) << 12) + | sl->first_mb_in_slice % (sps->pic_width_in_mbs_minus1 + 1), + "cm3_cmd_exec_mb_vp"); + + push(0x2d900000 + | ( ((26 + pps->pic_init_qp_minus26 + sl->slice_qp_delta) * 0x400) + & 0x1fc00), + "slc_a70_cmd_quant_param"); + + push(0x2da00000 + | (u32)(sl->disable_deblocking_filter_idc == 0 + ? BIT(17) : 0) + | (u32)(sl->disable_deblocking_filter_idc != 1 + ? BIT(16) + | swrap(sl->slice_beta_offset_div2, 16) << 12 + | swrap(sl->slice_alpha_c0_offset_div2, 16) << 8 + : 0), + "slc_a74_cmd_deblocking_filter"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_P + || sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + + u32 num_ref_idx_active = sl->num_ref_idx_l0_active_minus1 + 1; + for (u32 i = 0; i < num_ref_idx_active; i++) + push(0x2dc00000 + | (0 << 8) + | ((i & 0xf) << 4) + | (sl->ref_pic_list0[i].index & 0xf), + "slc_6e8_cmd_ref_list_0"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + u32 num_ref_idx_active = sl->num_ref_idx_l1_active_minus1 + 1; + for (u32 i = 0; i < num_ref_idx_active; i++) + push(0x2dc00000 + | (1 << 8) + | ((i & 0xf) << 4) + | (sl->ref_pic_list1[i].index & 0xf), + "slc_6e8_cmd_ref_list_0"); + } + stream_weights(ctx, run); + } + + if (sl->first_mb_in_slice == 0) { + push(0x2a000000, "cm3_cmd_set_mb_dims"); + push(((sps->pic_height_in_map_units_minus1) << 12) + | (sps->pic_width_in_mbs_minus1), "cm3_set_mb_dims"); + } + + x = 0; + if (sl->slice_type == V4L2_H264_SLICE_TYPE_I) + x |= 0x20000; + else if (sl->slice_type == V4L2_H264_SLICE_TYPE_P) + x |= 0x10000; + else if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) + x |= 0x40000; + + if ((sl->slice_type == V4L2_H264_SLICE_TYPE_P) || + (sl->slice_type == V4L2_H264_SLICE_TYPE_B)) { + if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) + x |= sl->cabac_init_idc << 5; + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + x |= sl->num_ref_idx_l1_active_minus1 << 7; + if (!(sl->flags & + V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED)) + x |= BIT(15); + } + x |= sl->num_ref_idx_l0_active_minus1 << 11; + } + push(0x2d000000 | x, "slc_6e4_cmd_ref_type"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + /* bidirectional reference of previous mv */ + struct vb2_buffer *vb = vb2_find_buffer( + &ctx->fh.m2m_ctx->cap_q_ctx.q, + decode->dpb[sl->ref_pic_list1[0].index].reference_ts); + + dma_addr_t sps_tile_addr = vb + ? vb2_dma_contig_plane_dma_addr(vb, 0) + + (vb->planes[0].length - sps_size(fmt_width(ctx), fmt_height(ctx))) + : run->addresses.sps; + + pusha(sps_tile_addr, "slc_a78_sps_tile_addr2_lsb8", 0); + } + + /* only submit if this is the last slice */ + push(0x2b000000 + | !(src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) << 10, + "cm3_cmd_inst_fifo_end"); + + return payload_len - off; +} +/* clang-format on */ + +static int avd_h264_alloc_bufs(struct avd_ctx *ctx) +{ + struct avd_dev *dev = ctx->dev; + struct avd_h264_ctx *h264_ctx = ctx->priv; + int ret; + + ret = avd_buf_alloc(dev, &h264_ctx->bufs.inst, fifo_size()); + if (ret) { + dev_err(dev->dev, "inst alloc failed\n"); + return ret; + } + + if (!(dev->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) { + ret = avd_buf_alloc(dev, &h264_ctx->bufs.pipe_state, 0x200); + if (ret) { + dev_err(dev->dev, "pipe state alloc failed\n"); + return ret; + } + } + + /* TODO: VERY ugly + * Does it actually need this much? + * */ + for (int i = 0; i < 5; i++) { + u32 mul = fmt_width(ctx) / 16; + u32 size = i == 0 ? 0x20000 : + i == 1 ? (16 + 8 + 8) * mul : + i == 2 ? ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline > 2048 ? + 0xc000 : + (64 + 1 * 32 + 1 * 32) * mul : + 32 * mul; + ret = avd_buf_alloc(dev, &h264_ctx->bufs.pps_tile[i], + max(size, 0x8000)); + if (ret) { + dev_err(dev->dev, "pps[%d] alloc failed\n", i); + return ret; + } + } + + return 0; +} + +static void avd_h264_free_bufs(struct avd_ctx *ctx) +{ + struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_dev *dev = ctx->dev; + + if (!h264_ctx) + return; + + if (!(dev->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) + avd_buf_free(dev, &h264_ctx->bufs.pipe_state); + avd_buf_free(dev, &h264_ctx->bufs.inst); + + for (int i = 0; i < 5; i++) + avd_buf_free(dev, &h264_ctx->bufs.pps_tile[i]); + + kfree(h264_ctx); +} + +static int avd_h264_validate_pps(struct avd_ctx *ctx, + const struct v4l2_ctrl_h264_pps *pps) +{ + if (pps->num_slice_groups_minus1 != 0) { + dev_err(ctx->dev->dev, "pps->num_slice_groups_minus1 != 0"); + return -EINVAL; + } + + return 0; +} + +static int avd_h264_validate_sps(struct avd_ctx *ctx, + const struct v4l2_ctrl_h264_sps *sps) +{ + if (sps->chroma_format_idc > 2) + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + /* no interlaced support */ + return -EINVAL; + + return 0; +} + +static int avd_h264_start(struct avd_ctx *ctx) +{ + struct avd_h264_ctx *h264_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_H264_SPS); + if (!ctrl) + return -EINVAL; + + ret = avd_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + if (ret) + return ret; + + h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL); + if (!h264_ctx) + return -ENOMEM; + + ctx->priv = h264_ctx; + + ret = avd_h264_alloc_bufs(ctx); + if (ret) + goto err_free_ctx; + + return 0; + +err_free_ctx: + kfree(h264_ctx); + ctx->priv = NULL; + return ret; +} + +static void avd_h264_stop(struct avd_ctx *ctx) +{ + avd_h264_free_bufs(ctx); + + /* needed for all so automatic? */ + free_vp_slot(ctx->dev, ctx); + free_inst_slot(ctx->dev, ctx); +} + +static void avd_h264_run_preamble(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + struct v4l2_ctrl *ctrl; + u32 dst_len, sps_len; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_DECODE_PARAMS); + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + run->sps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_PPS); + run->pps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SLICE_PARAMS); + run->slice_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SCALING_MATRIX); + run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_PRED_WEIGHTS); + run->pred_weights = ctrl ? ctrl->p_cur.p : NULL; + + avd_run_preamble(ctx, &run->base); + + dst_len = run->base.bufs.dst->vb2_buf.planes[0].length; + + run->addresses.y = + vb2_dma_contig_plane_dma_addr(&run->base.bufs.dst->vb2_buf, 0); + + run->addresses.uv = + run->addresses.y + + ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline * + ALIGN(ctx->decoded_fmt.fmt.pix_mp.height, 16); + + run->addresses.sl = + vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0); + sps_len = sps_size(fmt_width(ctx), fmt_height(ctx)); + + run->addresses.rvra = + run->addresses.y + (dst_len - sps_len - ctx->rvra.size); + + run->addresses.sps = run->addresses.y + (dst_len - sps_len); +} + +static int avd_h264_run(struct avd_ctx *ctx) +{ + struct avd_dev *avd = ctx->dev; + struct avd_h264_ctx *h264_ctx = ctx->priv; + struct v4l2_h264_reflist_builder reflist_builder; + struct avd_h264_run run; + u32 slice_size, slice_parsed, reg; + int ret; + + avd_h264_run_preamble(ctx, &run); + + /* Build the P/B{0,1} ref lists. */ + v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params, run.sps, + run.decode_params->dpb); + + run.num_valid = reflist_builder.num_valid; + run.cur_poc = reflist_builder.cur_pic_order_count; + + v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); + v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, + h264_ctx->reflists.b1); + + avd_run_postamble(ctx, &run.base); + + if (is_new_frame(run.slice_params)) { + ret = alloc_slots(avd, ctx, AVD_CODEC_H264); + if (ret) { + dev_err_ratelimited(avd->dev, "no free slots: %d", ret); + return ret; + } + avd->variant->configure_stream(ctx->dev, h264_ctx->bufs.inst.addr, + ctx->fifo_idx, ctx->vp_slot); + stream_hdr(ctx, &run); + } + + if (ctx->vp_slot == VP_SLOT_NONE) { + /* Only happens if its a multi slice frame and there was an error */ + dev_err_ratelimited(avd->dev, "no assigned VP slots: %04lx", avd->vp_slots); + return -ENOMEM; + } + + schedule_delayed_work(&ctx->watchdog_work, msecs_to_jiffies(2000)); + + slice_size = stream_slice(ctx, &run); + + if (run.base.bufs.src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) { + if (avd->variant->revision == 3) + reg = (0x18 | ctx->vp_slot << 12); + else + reg = (0x1018 | ctx->vp_slot << 8); + + /* seems to be take ~ slice_size / 16 us */ + ret = readl_poll_timeout( + avd->ctrl + reg, slice_parsed, + slice_parsed >= round_down(slice_size, 8), 5, 1000); + + if (ret) { + dev_err(avd->dev, "VP%d: timed out (%02d)! size: %08x parsed: %08x", + ctx->vp_slot, ctx->fifo_idx, slice_size, slice_parsed); + avd_status(avd, ctx->vp_slot); + return 0; + } + + if (cancel_delayed_work(&ctx->watchdog_work)) + avd_job_finish(ctx, VB2_BUF_STATE_DONE); + } + + return 0; +} + +static enum avd_image_fmt avd_h264_get_image_fmt(struct avd_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) + return AVD_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return AVD_IMG_FMT_422_8BIT; + else + return AVD_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return AVD_IMG_FMT_422_10BIT; + else + return AVD_IMG_FMT_420_10BIT; + } + + return AVD_IMG_FMT_ANY; +} + +static void avd_h264_adjust_decoded_fmt(struct avd_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + pix_mp->plane_fmt[0].sizeimage += sps_size(pix_mp->width, pix_mp->height); +} + +static int avd_h264_try_ctrl(struct avd_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) + return avd_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + if (ctrl->id == V4L2_CID_STATELESS_H264_PPS) + return avd_h264_validate_pps(ctx, ctrl->p_new.p_h264_pps); + + return 0; +} + +static void avd_h264_submit(struct avd_ctx *ctx) +{ + writel_relaxed(0x2b000000 + | (ctx->dev->variant->revision == 3 ? 0x100 : 0x200) + | (ctx->fifo_idx << 4) + | ctx->dev->variant->fifo_slots, + ctx->dev->ctrl + ctx->dev->variant->submit_offset); +} + +const struct avd_coded_fmt_ops avd_h264_fmt_ops = { + .adjust_decoded_fmt = avd_h264_adjust_decoded_fmt, + .start = avd_h264_start, + .stop = avd_h264_stop, + .run = avd_h264_run, + .submit = avd_h264_submit, + .try_ctrl = avd_h264_try_ctrl, + .get_image_fmt = avd_h264_get_image_fmt, +}; diff --git a/drivers/media/platform/apple/avd/avd-hw.c b/drivers/media/platform/apple/avd/avd-hw.c new file mode 100644 index 00000000000000..168d68cd2cf845 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-hw.c @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: MIT */ + +#include "linux/dev_printk.h" +#include + +#include "avd.h" +#include "avd-regs.h" + +/* The plan is to move this to the cm3 */ + +#define AVD_V3_VP_INSN_FIFO_IOVA 0x4068 +#define AVD_V3_VP_INSN_FIFO_MASK 0x4084 +#define AVD_V3_VP_INSN_FIFO_CACH 0x40a0 +#define AVD_V3_VP_INSN_FIFO_XFER 0x40bc +#define AVD_V3_VP_CTRL_UNK 0x4040 +#define AVD_V3_CTRL_CM3_IRQ_MASK 0x405c + +#define AVD_V4_VP_INSN_FIFO_IOVA_HI 0x30c +#define AVD_V4_VP_INSN_FIFO_IOVA_LO 0x150 +#define AVD_V4_VP_INSN_FIFO_MASK 0x18c +#define AVD_V4_VP_INSN_FIFO_CACH 0x1c8 +#define AVD_V4_VP_INSN_FIFO_XFER 0x204 +#define AVD_V4_VP_CTRL_CM3_IRQ_MASK 0x0fc +#define AVD_V4_PP_CTRL_CM3_IRQ_MASK 0x120 + +/* seems stabile after this */ +#define AVD_V5_VP_INSN_FIFO_IOVA_HI 0x20c +#define AVD_V5_VP_INSN_FIFO_IOVA_LO 0x1d0 +#define AVD_V5_VP_INSN_FIFO_MASK 0x248 +#define AVD_V5_VP_INSN_FIFO_CACH 0x284 +#define AVD_V5_VP_INSN_FIFO_XFER 0x2c0 +#define AVD_V5_VP_CTRL_CM3_IRQ_MASK 0x15c +#define AVD_V5_PP_CTRL_CM3_IRQ_MASK 0x190 + +#define AVD_CM3_UNK BIT(0) +#define AVD_CM3_ERROR BIT(1) +#define AVD_CM3_DONE BIT(2) +#define AVD_CM3_FULL BIT(3) + +#define AVD_VP_CM3_MASK (AVD_CM3_UNK | AVD_CM3_ERROR | AVD_CM3_DONE) +#define AVD_PP_CM3_MASK (AVD_CM3_UNK | AVD_CM3_DONE) + +int avd_boot(struct avd_dev *avd) +{ + u32 val; + int ret; + + if (avd->variant->revision != 3) + dev_info_once(avd->dev, "booting hw version: %04x", + readl_relaxed(avd->ctrl)); + + memcpy_toio(avd->code, avd->fw->data, avd->fw->size); + + writel_relaxed(AVD_MBOX_ENABLE, avd->mbox + AVD_REG_MBOX1_STATUS); + writel_relaxed(AVD_MBOX1_NOT_EMPTY, avd->mbox + AVD_REG_MBOX_IRQ_ENABLE); + writel_relaxed(AVD_RUN_CTRL_UNK_RUN, avd->mbox + AVD_REG_RUN_CTRL); + + /* wait for cm3 to boot */ + ret = readl_poll_timeout(avd->mbox + AVD_REG_FLAG0_SET, + val, val == 1, 10, 10000); + if (ret) + return ret; + + return 0; +} + +void avd_shutdown(struct avd_dev *avd) +{ + writel_relaxed(AVD_RUN_CTRL_UNK_STOP, avd->mbox + AVD_REG_RUN_CTRL); + writel_relaxed(1, avd->mbox + AVD_REG_FLAG0_CLR); + writel_relaxed(0, avd->mbox + AVD_REG_MBOX_IRQ_ENABLE); +} + +#define w32(reg, val) (writel_relaxed(val, avd->ctrl + (reg))) +#define m32(reg, val) (w32(reg, (val) | readl_relaxed(avd->ctrl + (reg)))) + +void t8103_configure_stream(struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot) +{ + w32(AVD_V3_VP_INSN_FIFO_IOVA + (fifo_idx * 4), addr >> 8); + w32(AVD_V3_VP_INSN_FIFO_MASK + (fifo_idx * 4), 0x100000); + w32(AVD_V3_VP_INSN_FIFO_CACH + (fifo_idx * 4), 0); + w32(AVD_V3_VP_INSN_FIFO_XFER + (fifo_idx * 4), 0); + + w32(AVD_V3_VP_CTRL_UNK + (vp_slot * 4), 0); + m32(AVD_V3_CTRL_CM3_IRQ_MASK, + AVD_VP_CM3_MASK << (vp_slot * 5) | (AVD_PP_CM3_MASK << 20)); +} + +void t8112_configure_stream(struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot) +{ + if (avd->variant->quirks & AVD_QUIRK_LSR) { + w32(AVD_V4_VP_INSN_FIFO_IOVA_LO + (fifo_idx * 4), addr >> 8); + } else { + w32(AVD_V4_VP_INSN_FIFO_IOVA_HI + (fifo_idx * 4), addr >> 32); + w32(AVD_V4_VP_INSN_FIFO_IOVA_LO + (fifo_idx * 4), addr & 0xffffffff); + } + w32(AVD_V4_VP_INSN_FIFO_MASK + (fifo_idx * 4), 0); + w32(AVD_V4_VP_INSN_FIFO_CACH + (fifo_idx * 4), 0); + w32(AVD_V4_VP_INSN_FIFO_XFER + (fifo_idx * 4), 0); + + m32(AVD_V4_VP_CTRL_CM3_IRQ_MASK + (vp_slot * 4), AVD_VP_CM3_MASK); + m32(AVD_V4_PP_CTRL_CM3_IRQ_MASK, AVD_PP_CM3_MASK); +} + +void t8122_configure_stream(struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot) +{ + w32(AVD_V5_VP_INSN_FIFO_IOVA_HI + (fifo_idx * 4), addr >> 32); + w32(AVD_V5_VP_INSN_FIFO_IOVA_LO + (fifo_idx * 4), addr & 0xffffffff); + w32(AVD_V5_VP_INSN_FIFO_MASK + (fifo_idx * 4), 0); + w32(AVD_V5_VP_INSN_FIFO_CACH + (fifo_idx * 4), 0); + w32(AVD_V5_VP_INSN_FIFO_XFER + (fifo_idx * 4), 0); + + m32(AVD_V5_VP_CTRL_CM3_IRQ_MASK + (vp_slot * 4), AVD_VP_CM3_MASK); + m32(AVD_V5_PP_CTRL_CM3_IRQ_MASK, AVD_PP_CM3_MASK); +} + +void avd_status(struct avd_dev *avd, u32 vp) +{ + /* + * the command 0x2d000000 resets theese + * + * 0 config 0x80000000 or 0xc0000000 + * 1 related to buffers? example 0x0e6733a0 + * 2 unkown zero + * 3 unkown zero + * 4 mb parsed? starts at zero same as h264 cm3_set_mb_dims + * 5 status 0x3f on succes, could be usefull + * 6 slice bytes parsed + * 7 insn bytes written/parsed + */ + u32 start; + u32 val[8]; + if (avd->variant->revision == 3) + start = vp << 12; + else + start = 0x1000 | (vp << 8); + + for (int i = 0; i < 8; i++) + val[i] = readl_relaxed(avd->ctrl + start + (i * 4)); + + dev_info(avd->dev, "VP%d: %08x %08x %08x %08x", vp, val[0], val[1], + val[2], val[3]); + dev_info(avd->dev, "VP%d: %08x %08x %08x %08x", vp, val[4], val[5], + val[6], val[7]); +} + +#undef w32 +#undef r32 +#undef m32 diff --git a/drivers/media/platform/apple/avd/avd-inst.h b/drivers/media/platform/apple/avd/avd-inst.h new file mode 100644 index 00000000000000..78d7e37f60bac8 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-inst.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef AVD_INST_H_ +#define AVD_INST_H_ +/* instruction stream things */ +#include + +#include "avd.h" + +/* i have no clue what this is */ +#define INST_DMA1 0 /* (0x14 << 16 | 0x14) */ +#define INST_DMA2 0 /* (0x4000000 | INST_DMA1) */ +#define INST_DMA3 0 /* (0x07 << 16 | 0x07) */ + +static inline u32 fifo_size(void) +{ + return 0x100000 * 12; +} + +static inline u32 swrap(u32 x, u32 w) +{ + return x & (w - 1); +} +static inline bool boolify(u32 v) +{ + return !!(v); +} + +static inline void push(struct avd_dev *avd, struct avd_ctx *ctx, u32 inst) +{ + writel(inst, avd->ctrl + avd->variant->vp_slot_offset + ctx->vp_slot * 4); +} + +static inline void push_address(struct avd_dev *avd, struct avd_ctx *ctx, + dma_addr_t addr) +{ + if (avd->variant->quirks & AVD_QUIRK_LSR) { + push(avd, ctx, (addr >> 8)); + } else { + push(avd, ctx, (u32)(addr & 0xffffffff)); + push(avd, ctx, (u32)(addr >> 32)); + } +} +static inline void push_rvra(struct avd_dev *avd, struct avd_ctx *ctx, + dma_addr_t addr, u32 offsets[4]) +{ + if (avd->variant->quirks & AVD_QUIRK_LSR) { + for (int i = 0; i < 4; i++) + push(avd, ctx, (addr + offsets[i]) >> 7); + } else { + for (int i = 0; i < 4; i++) + push_address(avd, ctx, (addr + offsets[i])); + } +} + +#ifdef DEBUG_INST +#define push(inst, name) \ + do { \ + dev_info(ctx->dev->dev, "%8x | %s", (inst), name); \ + push(avd, ctx, inst); \ + } while (0) + +#else +#define push(inst, name) push(avd, ctx, inst) +#endif + +#ifdef DEBUG_INST_ADDR +#define pusha(inst, name, i) \ + do { \ + dev_info(ctx->dev->dev, "%8llx | %s[%d]", (inst) & 0xffffffff, \ + name, i); \ + dev_info(ctx->dev->dev, "%8llx | %s[%d] (high)", (inst) >> 32, \ + name, i); \ + push_address(avd, ctx, inst); \ + } while (0) + +#else +#define pusha(inst, name, i) push_address(avd, ctx, inst) +#endif + +#endif /* AVD_INST_H_ */ diff --git a/drivers/media/platform/apple/avd/avd-regs.h b/drivers/media/platform/apple/avd/avd-regs.h new file mode 100644 index 00000000000000..388f3e64895f58 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-regs.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef AVD_REGS_H_ +#define AVD_REGS_H_ + +#define AVD_REG_RUN_CTRL 0x08 +#define AVD_RUN_CTRL_UNK_RUN BIT(0) +#define AVD_RUN_CTRL_UNK_STOP BIT(1) | BIT(2) | BIT(3) + +#define AVD_REG_MBOX_IRQ_ENABLE 0x48 +#define AVD_REG_MBOX_IRQ_CLR 0x4c +#define AVD_MBOX1_EMPTY BIT(2) +#define AVD_MBOX1_NOT_EMPTY BIT(3) + +#define AVD_REG_MBOX1_STATUS 0x5c +#define AVD_REG_MBOX1_RETRIEVE 0x64 +#define AVD_MBOX_ENABLE BIT(0) + +#define AVD_REG_FLAG0_SET 0x90 +#define AVD_REG_FLAG0_CLR 0x98 + +#endif /* AVD_REGS_H_ */ diff --git a/drivers/media/platform/apple/avd/avd-v4l2.c b/drivers/media/platform/apple/avd/avd-v4l2.c new file mode 100644 index 00000000000000..6f690761844a36 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-v4l2.c @@ -0,0 +1,950 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple Video Decoder driver + * + * Copyright The Asahi Linux Contributors + * + * Based on rkvdec driver by Collabora, Ltd. + * Copyright (C) 2019 Collabora, Ltd. + * Based on rkvdec driver by Google LLC. (Tomasz Figa ) + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#include + +#include +#include + +#include "avd.h" + +static bool avd_image_fmt_match(enum avd_image_fmt fmt1, + enum avd_image_fmt fmt2) +{ + return fmt1 == fmt2 || fmt2 == AVD_IMG_FMT_ANY || + fmt1 == AVD_IMG_FMT_ANY; +} + +static bool avd_image_fmt_changed(struct avd_ctx *ctx, + enum avd_image_fmt image_fmt) +{ + if (image_fmt == AVD_IMG_FMT_ANY) + return false; + + return ctx->image_fmt != image_fmt; +} + +static u32 avd_enum_decoded_fmt(struct avd_ctx *ctx, int index, + enum avd_image_fmt image_fmt) +{ + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + int fmt_idx = -1; + unsigned int i; + + if (WARN_ON(!desc)) + return 0; + + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (!avd_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt)) + continue; + fmt_idx++; + if (index == fmt_idx) + return desc->decoded_fmts[i].fourcc; + } + + return 0; +} + +static bool avd_is_valid_fmt(struct avd_ctx *ctx, u32 fourcc, + enum avd_image_fmt image_fmt) +{ + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + unsigned int i; + + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (avd_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt) && + desc->decoded_fmts[i].fourcc == fourcc) + return true; + } + + return false; +} + +static void avd_fill_decoded_pixfmt(struct avd_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, pix_mp->width, + pix_mp->height); + fill_rvra(&ctx->rvra, ctx->image_fmt, pix_mp->width, pix_mp->height); + pix_mp->plane_fmt[0].sizeimage += ctx->rvra.size; + if (ctx->coded_fmt_desc->ops->adjust_decoded_fmt) + ctx->coded_fmt_desc->ops->adjust_decoded_fmt(ctx, pix_mp); +} + +static void avd_reset_fmt(struct avd_ctx *ctx, struct v4l2_format *f, + u32 fourcc) +{ + memset(f, 0, sizeof(*f)); + f->fmt.pix_mp.pixelformat = fourcc; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +void avd_reset_decoded_fmt(struct avd_ctx *ctx) +{ + struct v4l2_format *f = &ctx->decoded_fmt; + u32 fourcc; + + fourcc = avd_enum_decoded_fmt(ctx, 0, ctx->image_fmt); + avd_reset_fmt(ctx, f, fourcc); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; + f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; + avd_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); +} + +static int avd_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct avd_ctx *ctx = + container_of(ctrl->handler, struct avd_ctx, ctrl_hdl); + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + + if (desc->ops->try_ctrl) + return desc->ops->try_ctrl(ctx, ctrl); + + return 0; +} + +static int avd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct avd_ctx *ctx = + container_of(ctrl->handler, struct avd_ctx, ctrl_hdl); + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + enum avd_image_fmt image_fmt; + struct vb2_queue *vq; + + /* Check if this change requires a capture format reset */ + if (!desc->ops->get_image_fmt) + return 0; + + image_fmt = desc->ops->get_image_fmt(ctx, ctrl); + if (avd_image_fmt_changed(ctx, image_fmt)) { + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->image_fmt = image_fmt; + avd_reset_decoded_fmt(ctx); + } + + return 0; +} + +const struct v4l2_ctrl_ops avd_ctrl_ops = { + .try_ctrl = avd_try_ctrl, + .s_ctrl = avd_s_ctrl, +}; + +static const struct avd_ctrl_desc avd_hevc_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, + .cfg.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY, + .cfg.type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS, + .cfg.dims = { 600 }, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SPS, + .cfg.ops = &avd_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_PPS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, + .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, + .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE, + .cfg.min = V4L2_STATELESS_HEVC_START_CODE_NONE, + .cfg.def = V4L2_STATELESS_HEVC_START_CODE_NONE, + .cfg.max = V4L2_STATELESS_HEVC_START_CODE_NONE, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, + }, +}; + +static const struct avd_ctrls avd_hevc_ctrls = { + .ctrls = avd_hevc_ctrl_descs, + .num_ctrls = ARRAY_SIZE(avd_hevc_ctrl_descs), +}; + +static const struct avd_decoded_fmt_desc avd_hevc_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = AVD_IMG_FMT_420_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV15, + .image_fmt = AVD_IMG_FMT_420_10BIT, + }, +}; + +static const struct avd_ctrl_desc avd_h264_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SPS, + .cfg.ops = &avd_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_PPS, + .cfg.ops = &avd_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, + .cfg.min = V4L2_STATELESS_H264_START_CODE_NONE, + .cfg.max = V4L2_STATELESS_H264_START_CODE_NONE, + .cfg.def = V4L2_STATELESS_H264_START_CODE_NONE, + /* annex b is also possibly but a bit more painfull */ + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, + .cfg.menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), + .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + }, +}; + +static const struct avd_ctrls avd_h264_ctrls = { + .ctrls = avd_h264_ctrl_descs, + .num_ctrls = ARRAY_SIZE(avd_h264_ctrl_descs), +}; + +static const struct avd_decoded_fmt_desc avd_h264_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = AVD_IMG_FMT_420_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_P010, + .image_fmt = AVD_IMG_FMT_420_10BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .image_fmt = AVD_IMG_FMT_422_8BIT, + }, + { + /* TODO: missing P210 */ + .fourcc = V4L2_PIX_FMT_P010, + .image_fmt = AVD_IMG_FMT_422_10BIT, + }, +}; + +static const struct avd_ctrl_desc avd_vp9_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_VP9_FRAME, + }, + { + .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_3, + .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + }, +}; + +static const struct avd_ctrls avd_vp9_ctrls = { + .ctrls = avd_vp9_ctrl_descs, + .num_ctrls = ARRAY_SIZE(avd_vp9_ctrl_descs), +}; + +static const struct avd_decoded_fmt_desc avd_vp9_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = AVD_IMG_FMT_420_8BIT, + }, + /* + * TODO: it looks like gstreamer uses h264 to decide what format to use + * ffmpeg handles it fine + { + .fourcc = V4L2_PIX_FMT_P010, + .image_fmt = AVD_IMG_FMT_420_10BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .image_fmt = AVD_IMG_FMT_422_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_P010, + .image_fmt = AVD_IMG_FMT_422_10BIT, + }, + */ +}; + +static const struct avd_coded_fmt_desc avd_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 16, + .min_height = 64, + .max_height = 4096, + .step_height = 16, + }, + .ctrls = &avd_hevc_ctrls, + .ops = &avd_hevc_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(avd_hevc_decoded_fmts), + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + .decoded_fmts = avd_hevc_decoded_fmts, + .capability = AVD_CAPABILITY_HEVC, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 64, + .max_height = 4096, + .step_height = 16, + }, + .ctrls = &avd_h264_ctrls, + .ops = &avd_h264_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(avd_h264_decoded_fmts), + .decoded_fmts = avd_h264_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + .capability = AVD_CAPABILITY_H264, + }, + { + .fourcc = V4L2_PIX_FMT_VP9_FRAME, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 64, + .max_height = 4096, + .step_height = 16, + }, + .ctrls = &avd_vp9_ctrls, + .ops = &avd_vp9_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(avd_vp9_decoded_fmts), + .decoded_fmts = avd_vp9_decoded_fmts, + .capability = AVD_CAPABILITY_VP9, + } +}; + +static bool avd_is_capable(struct avd_ctx *ctx, unsigned int capability) +{ + return capability & (AVD_CAPABILITY_H264 | AVD_CAPABILITY_VP9); + /* return (ctx->dev->variant->capabilities & capability) == capability; */ +} + +static const struct avd_coded_fmt_desc * +avd_enum_coded_fmt_desc(struct avd_ctx *ctx, int index) +{ + int fmt_idx = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(avd_coded_fmts); i++) { + if (!avd_is_capable(ctx, avd_coded_fmts[i].capability)) + continue; + fmt_idx++; + if (index == fmt_idx) + return &avd_coded_fmts[i]; + } + + return NULL; +} + +static const struct avd_coded_fmt_desc * +avd_find_coded_fmt_desc(struct avd_ctx *ctx, u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(avd_coded_fmts); i++) { + if (avd_is_capable(ctx, avd_coded_fmts[i].capability) && + avd_coded_fmts[i].fourcc == fourcc) + return &avd_coded_fmts[i]; + } + + return NULL; +} + +void avd_reset_coded_fmt(struct avd_ctx *ctx) +{ + struct v4l2_format *f = &ctx->coded_fmt; + + ctx->coded_fmt_desc = avd_enum_coded_fmt_desc(ctx, 0); + avd_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); + + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; + f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; + + f->fmt.pix_mp.num_planes = 1; + if (!f->fmt.pix_mp.plane_fmt[0].sizeimage) + f->fmt.pix_mp.plane_fmt[0].sizeimage = + f->fmt.pix_mp.width * f->fmt.pix_mp.height; +} + +static int avd_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct avd_ctx *ctx = file_to_ctx(file); + const struct avd_coded_fmt_desc *desc; + + if (fsize->index != 0) + return -EINVAL; + + desc = avd_find_coded_fmt_desc(ctx, fsize->pixel_format); + if (!desc) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = desc->frmsize.max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = 1; + fsize->stepwise.max_height = desc->frmsize.max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int avd_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct avd_dev *avd = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, avd->dev->driver->name, sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + avd->dev->driver->name); + return 0; +} + +static int avd_try_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct avd_ctx *ctx = file_to_ctx(file); + const struct avd_coded_fmt_desc *coded_desc; + + /* + * The codec context should point to a coded format desc, if the format + * on the coded end has not been set yet, it should point to the + * default value. + */ + coded_desc = ctx->coded_fmt_desc; + if (WARN_ON(!coded_desc)) { + dev_err(ctx->dev->dev, "no coded desc!"); + return -EINVAL; + } + + if (!avd_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt)) + pix_mp->pixelformat = + avd_enum_decoded_fmt(ctx, 0, ctx->image_fmt); + + /* Always apply the frmsize constraint of the coded end. */ + pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); + pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height); + v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, + &coded_desc->frmsize); + + avd_fill_decoded_pixfmt(ctx, pix_mp); + pix_mp->field = V4L2_FIELD_NONE; + + return 0; +} + +static int avd_try_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct avd_ctx *ctx = file_to_ctx(file); + const struct avd_coded_fmt_desc *desc; + + desc = avd_find_coded_fmt_desc(ctx, pix_mp->pixelformat); + if (!desc) { + desc = avd_enum_coded_fmt_desc(ctx, 0); + pix_mp->pixelformat = desc->fourcc; + } + + v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, + &desc->frmsize); + + pix_mp->field = V4L2_FIELD_NONE; + /* All coded formats are considered single planar for now. */ + pix_mp->num_planes = 1; + + if (!pix_mp->plane_fmt[0].sizeimage) + pix_mp->plane_fmt[0].sizeimage = + pix_mp->width * f->fmt.pix_mp.height; + + return 0; +} + +static int avd_s_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + struct vb2_queue *vq; + int ret; + + /* Change not allowed if queue is busy */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ret = avd_try_capture_fmt(file, priv, f); + if (ret) + return ret; + + ctx->decoded_fmt = *f; + return 0; +} + +static int avd_s_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct avd_coded_fmt_desc *desc; + struct v4l2_format *cap_fmt; + struct vb2_queue *peer_vq, *vq; + int ret; + + /* + * In order to support dynamic resolution change, the decoder admits + * a resolution change, as long as the pixelformat remains. Can't be + * done if streaming. + */ + vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_streaming(vq) || + (vb2_is_busy(vq) && f->fmt.pix_mp.pixelformat != + ctx->coded_fmt.fmt.pix_mp.pixelformat)) + return -EBUSY; + + /* + * Since format change on the OUTPUT queue will reset the CAPTURE + * queue, we can't allow doing so when the CAPTURE queue has buffers + * allocated. + */ + peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(peer_vq)) + return -EBUSY; + + ret = avd_try_output_fmt(file, priv, f); + if (ret) + return ret; + + desc = avd_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat); + if (!desc) + return -EINVAL; + ctx->coded_fmt_desc = desc; + ctx->coded_fmt = *f; + + /* + * Current decoded format might have become invalid with newly + * selected codec, so reset it to default just to be safe and + * keep internal driver state sane. User is mandated to set + * the decoded format again after we return, so we don't need + * anything smarter. + * + * Note that this will propagates any size changes to the decoded format. + */ + ctx->image_fmt = AVD_IMG_FMT_ANY; + avd_reset_decoded_fmt(ctx); + + /* Propagate colorspace information to capture. */ + cap_fmt = &ctx->decoded_fmt; + cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; + cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; + cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + + /* Enable format specific queue features */ + vq->subsystem_flags |= desc->subsystem_flags; + + return 0; +} + +static int avd_g_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + + *f = ctx->coded_fmt; + return 0; +} + +static int avd_g_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + + *f = ctx->decoded_fmt; + return 0; +} + +static int avd_enum_output_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + const struct avd_coded_fmt_desc *desc; + + desc = avd_enum_coded_fmt_desc(ctx, f->index); + if (!desc) + return -EINVAL; + + f->pixelformat = desc->fourcc; + return 0; +} + +static int avd_enum_capture_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct avd_ctx *ctx = file_to_ctx(file); + u32 fourcc; + + fourcc = avd_enum_decoded_fmt(ctx, f->index, ctx->image_fmt); + if (!fourcc) + return -EINVAL; + + f->pixelformat = fourcc; + return 0; +} + +const struct v4l2_ioctl_ops avd_ioctl_ops = { + .vidioc_querycap = avd_querycap, + .vidioc_enum_framesizes = avd_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = avd_try_capture_fmt, + .vidioc_try_fmt_vid_out_mplane = avd_try_output_fmt, + .vidioc_s_fmt_vid_out_mplane = avd_s_output_fmt, + .vidioc_s_fmt_vid_cap_mplane = avd_s_capture_fmt, + .vidioc_g_fmt_vid_out_mplane = avd_g_output_fmt, + .vidioc_g_fmt_vid_cap_mplane = avd_g_capture_fmt, + .vidioc_enum_fmt_vid_out = avd_enum_output_fmt, + .vidioc_enum_fmt_vid_cap = avd_enum_capture_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, +}; + +static int avd_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_format *f; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + f = &ctx->coded_fmt; + else + f = &ctx->decoded_fmt; + + if (*num_planes) { + if (*num_planes != f->fmt.pix_mp.num_planes) + return -EINVAL; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int avd_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct avd_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_format *f; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + f = &ctx->coded_fmt; + else + f = &ctx->decoded_fmt; + + for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) { + u32 sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < sizeimage) + return -EINVAL; + } + + /* + * Buffer's bytesused must be written by driver for CAPTURE buffers. + * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets + * it to buffer length). + */ + if (V4L2_TYPE_IS_CAPTURE(vq->type)) + vb2_set_plane_payload(vb, 0, + f->fmt.pix_mp.plane_fmt[0].sizeimage); + + return 0; +} + +static void avd_buf_queue(struct vb2_buffer *vb) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int avd_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static void avd_buf_request_complete(struct vb2_buffer *vb) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); +} + +static int avd_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(q); + const struct avd_coded_fmt_desc *desc; + int ret; + + if (V4L2_TYPE_IS_CAPTURE(q->type)) + return 0; + + desc = ctx->coded_fmt_desc; + if (WARN_ON(!desc)) + return -EINVAL; + + if (desc->ops->start) { + ret = desc->ops->start(ctx); + if (ret) + return ret; + } + + return 0; +} + +static void avd_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(vq); + + while (true) { + struct vb2_v4l2_buffer *vbuf; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!vbuf) + break; + + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, + &ctx->ctrl_hdl); + v4l2_m2m_buf_done(vbuf, state); + } +} + +static void avd_stop_streaming(struct vb2_queue *q) +{ + struct avd_ctx *ctx = vb2_get_drv_priv(q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + const struct avd_coded_fmt_desc *desc = ctx->coded_fmt_desc; + + if (WARN_ON(!desc)) + return; + + if (desc->ops->stop) + desc->ops->stop(ctx); + } + + avd_queue_cleanup(q, VB2_BUF_STATE_ERROR); +} + +const struct vb2_ops avd_queue_ops = { + .queue_setup = avd_queue_setup, + .buf_prepare = avd_buf_prepare, + .buf_queue = avd_buf_queue, + .buf_out_validate = avd_buf_out_validate, + .buf_request_complete = avd_buf_request_complete, + .start_streaming = avd_start_streaming, + .stop_streaming = avd_stop_streaming, +}; + +void avd_job_finish_no_pm(struct avd_ctx *ctx, enum vb2_buffer_state result) +{ + if (ctx->coded_fmt_desc->ops->done) { + struct vb2_v4l2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result); + } + + v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, + result); +} + +void avd_job_finish(struct avd_ctx *ctx, enum vb2_buffer_state result) +{ + struct avd_dev *avd = ctx->dev; + + pm_runtime_put_autosuspend(avd->dev); + avd_job_finish_no_pm(ctx, result); +} + +void avd_run_preamble(struct avd_ctx *ctx, struct avd_run *run) +{ + struct media_request *src_req; + struct avd_decoded_buffer *dst; + + memset(run, 0, sizeof(*run)); + + run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + dst = vb2_to_avd_decoded_buf(&run->bufs.dst->vb2_buf); + memcpy(&dst->rvra, &ctx->rvra, sizeof(ctx->rvra)); + + /* Apply request(s) controls if needed. */ + src_req = run->bufs.src->vb2_buf.req_obj.req; + if (src_req) + v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl); + + v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst); +} + +void avd_run_postamble(struct avd_ctx *ctx, struct avd_run *run) +{ + struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req; + + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl); +} + +static int avd_add_ctrls(struct avd_ctx *ctx, const struct avd_ctrls *ctrls) +{ + unsigned int i; + + for (i = 0; i < ctrls->num_ctrls; i++) { + const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg; + + v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx); + if (ctx->ctrl_hdl.error) + return ctx->ctrl_hdl.error; + } + + return 0; +} + +int avd_init_ctrls(struct avd_ctx *ctx) +{ + unsigned int i, nctrls = 0; + int ret; + + for (i = 0; i < ARRAY_SIZE(avd_coded_fmts); i++) + if (avd_is_capable(ctx, avd_coded_fmts[i].capability)) + nctrls += avd_coded_fmts[i].ctrls->num_ctrls; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); + + for (i = 0; i < ARRAY_SIZE(avd_coded_fmts); i++) { + if (avd_is_capable(ctx, avd_coded_fmts[i].capability)) { + ret = avd_add_ctrls(ctx, avd_coded_fmts[i].ctrls); + if (ret) + goto err_free_handler; + } + } + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + if (ret) + goto err_free_handler; + + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + return ret; +} diff --git a/drivers/media/platform/apple/avd/avd-vp9.c b/drivers/media/platform/apple/avd/avd-vp9.c new file mode 100644 index 00000000000000..d70bee7fb5a66e --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-vp9.c @@ -0,0 +1,1015 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple AVD VPU codec driver + * + * Copyright The Asahi Linux Contributors + * Copyright 2023 Eileen Yoon + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon + * Copyright (C) 2021 Collabora, Ltd. + * Andrzej Pietrasiewicz + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Alpha Lin + */ + +#include "linux/v4l2-controls.h" +#include +#include + +#include +#include + +#include "avd.h" +#include "avd-inst.h" + +struct avd_vp9_seg_probs { + u8 tree_probs[7]; + u8 pred_probs[3]; +}; + +struct avd_vp9_probs { + struct avd_vp9_seg_probs seg; + u8 tx8[2][1]; + u8 tx16[2][2]; + u8 tx32[2][3]; + /* [4][2][2][k=6][(k == 0) ? 3 : 6][3] */ + u8 coef[1584]; + u8 skip[3]; + u8 inter_mode[7][3]; + u8 interp_filter[4][2]; + u8 is_inter[4]; + u8 comp_mode[5]; + u8 single_ref[5][2]; + u8 comp_ref[5]; + u8 y_mode[4][9]; + u8 uv_mode[10][9]; + u8 partition[16][3]; + u8 joint[3]; + struct mv_comp { + u8 sign; + u8 classes[10]; + u8 class0_bit; + u8 bits[10]; + } mv_comp[2]; + struct mv_fr { + u8 class0_fr[2][3]; + u8 fr[3]; + } mv_fr[2]; + struct mv_hp { + u8 class0_hp; + u8 hp; + } mv_hp[2]; +}; +static_assert(sizeof(struct avd_vp9_probs) == 1905); + +struct avd_vp9_frame_symbol_counts { + u32 padding; + u32 tx8p[2][2]; + u32 tx16p[2][3]; + u32 tx32p[2][4]; + /* [4][2][2][k=6][(k == 0) ? 3 : 6] */ + u32 eob_0[528]; + /* + * struct ref_cnt { u32 coef[3]; u32 eob_1; } + * struct ref_cnt cef_counts[4][2][2][k=6][(k == 0) ? 3 : 6]; + */ + u32 ref_cnt[2112]; + u32 skip[3][2]; + u32 mv_mode[7][4]; + u32 filter[4][3]; + u32 intra_inter[4][2]; + u32 comp[5][2]; + u32 single_ref[5][2][2]; + u32 comp_ref[5][2]; + u32 y_mode[4][10]; + u32 uv_mode[10][10]; + u32 partition[16][4]; + u32 mv_joint[4]; + struct mv_comp_ctn { + u32 sign[2]; + u32 classes[11]; + u32 class0[2]; + u32 bits[10][2]; + } mv_comp[2]; + struct mv_fr_cnt { + u32 class0_fr[2][4]; + u32 fr[4]; + } mv_fr[2]; + struct mv_hp_cnt { + u32 class0_hp[2]; + u32 hp[2]; + } mv_hp[2]; +}; +static_assert(sizeof(struct avd_vp9_frame_symbol_counts) == 12252); + +struct avd_vp9_frame_info { + u32 valid : 1; + u32 segmapid : 1; + u32 frame_context_idx : 2; + u32 reference_mode : 2; + u32 tx_mode : 3; + u32 interpolation_filter : 3; + u32 flags; + u64 timestamp; + struct v4l2_vp9_segmentation seg; + struct v4l2_vp9_loop_filter lf; +}; + +struct avd_vp9_run { + struct avd_run base; + + struct run_addr { + dma_addr_t y; + dma_addr_t uv; + dma_addr_t sl; + dma_addr_t rvra; + } addresses; + + const struct v4l2_ctrl_vp9_frame *decode_params; + const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; +}; + +struct avd_vp9_ctx { + struct v4l2_vp9_frame_symbol_counts cnts; + struct v4l2_vp9_frame_context probability_tables; + struct v4l2_vp9_frame_context frame_context[4]; + struct avd_vp9_bufs { + struct avd_buf inst; + /* only affect tiles */ + struct avd_buf tiles[3]; + /* just a guess (this name exists in tunables) */ + struct avd_buf above_info; + /* if above is true, state and color are left */ + struct avd_buf state; + struct avd_buf color[2]; + struct avd_buf seg; + struct avd_buf pipe_state; + struct avd_buf counts; + struct avd_buf probs; + } bufs; + struct avd_vp9_frame_info cur; + struct avd_vp9_frame_info last; + u8 submit_num; +}; + +static struct avd_decoded_buffer * +get_ref_buf(struct avd_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf; + + /* + * If a ref is unused or invalid, address of current destination + * buffer is returned. + */ + buf = vb2_find_buffer(cap_q, timestamp); + if (!buf) + buf = &dst->vb2_buf; + + return vb2_to_avd_decoded_buf(buf); +} + +static void set_refs(struct avd_ctx *ctx, struct avd_vp9_run *run) +{ + const struct v4l2_ctrl_vp9_frame *frame = run->decode_params; + struct avd_dev *avd = ctx->dev; + struct avd_decoded_buffer *dst, *ref_buf[4]; + dma_addr_t addr; + + dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf); + + ref_buf[0] = get_ref_buf(ctx, &dst->base.vb, frame->last_frame_ts); + ref_buf[1] = get_ref_buf(ctx, &dst->base.vb, frame->golden_frame_ts); + ref_buf[2] = get_ref_buf(ctx, &dst->base.vb, frame->alt_frame_ts); + + push(INST_DMA3, "cm3_dma_config_7"); + push(INST_DMA3, "cm3_dma_config_8"); + push(INST_DMA3, "cm3_dma_config_9"); + + for (int i = 0; i < V4L2_VP9_NUM_FRAME_CTX - 1; i++) { + + addr = vb2_dma_contig_plane_dma_addr(&ref_buf[i]->base.vb.vb2_buf, 0) + + (ref_buf[i]->base.vb.planes[0].length - ref_buf[i]->rvra.size); + + /* TODO */ + push(0x1000000, "hdr_9c_ref_100"); + push((ref_buf[i]->vp9.height - 1) << 16 | (ref_buf[i]->vp9.width - 1), + "hdr_70_ref_height_width"); + push(0x40004000, "hdr_7c_ref_align"); + + push_rvra(avd, ctx, addr, ref_buf[i]->rvra.offsets); + } +} + +static u32 make_flags1(struct avd_ctx *ctx, struct avd_vp9_run *run) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + const struct v4l2_ctrl_vp9_frame *frame = run->decode_params; + bool has_ref = boolify(vp9_ctx->last.valid && + !(vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) + && vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME); + + u32 flags = BIT(0) + | boolify(frame->flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE) << 14 + | !boolify(frame->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) << 15 + | boolify(frame->flags & V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV) << 19; + + if (!(frame->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME)) { + flags |= frame->interpolation_filter << 16; + if (!(frame->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) + flags |= has_ref << 21; + else + flags |= BIT(20); + } + + flags |= frame->ref_frame_sign_bias << 7; + /* this seems wrong */ + flags |= !(frame->golden_frame_ts == 0) << 10; + flags |= !(frame->ref_frame_sign_bias == 0) << 11; + + flags |= frame->reference_mode << 12; + + flags |= + !!(frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP) << 24 + | !!(frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) << 25; + /* what?? */ + if (frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA) { + if (frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE) + flags |= BIT(23); + else if (!vp9_ctx->last.valid) + flags |= BIT(26); + } + return flags; +} + +static u32 seg_features(struct avd_ctx *ctx, struct avd_vp9_run *run, + unsigned int segid) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + const struct v4l2_vp9_segmentation *seg = &vp9_ctx->cur.seg; + s16 feature_val = 0; + int feature_id = 0; + u32 enabled = 0; + + feature_id = V4L2_VP9_SEG_LVL_ALT_Q; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) + feature_val = seg->feature_data[segid][feature_id]; + + feature_id = V4L2_VP9_SEG_LVL_SKIP; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) + enabled |= 1; + + return (feature_val & 0x3ff) << 12 | enabled; +} + +static void set_header(struct avd_ctx *ctx, struct avd_vp9_run *run) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates = run->prob_updates; + const struct v4l2_ctrl_vp9_frame *frame = run->decode_params; + struct avd_dev *avd = ctx->dev; + u32 bytesperline; + + bool intra_only = !!(frame->flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | + V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); + + push(0x2b000000 + | (avd->variant->revision == 3 ? 0xfff100 : 0x200) + | (ctx->fifo_idx << 4), + "inst_fifo_start"); + + push(0x2db00000 + | 0x1000 + | ((intra_only) ? 0x2000 : 0) + | 0x2e0 + | (avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE ? 0 : 0x80000) + , "hdr_34_start_hdr"); + + push(AVD_CODEC_VP9 << 24, "hdr_38_mode"); + + push(((frame->frame_height_minus_1) << 16) | (frame->frame_width_minus_1), + "hdr_28_height_width_shift3"); + push(0, "cm3_dma_config_0"); + push(((frame->frame_height_minus_1) << 16) | (frame->frame_width_minus_1), + "hdr_38_height_width_shift3"); + + push(0x1000000 /* (sps->chroma_format_idc & 3) << 25 ? */ + | frame->profile << 19 + | (frame->bit_depth - 8) << 15 + | 0x1000 /* colorspace? */ + | 0x800 + | (min(prob_updates->tx_mode, 3) << 7) + | boolify(prob_updates->tx_mode & V4L2_VP9_TX_MODE_SELECT) + ,"hdr_2c_txfm_mode"); + + push(make_flags1(ctx, run), "hdr_40_flags1_pt1"); + + for (int i = 0; i < 8; i++) + push(seg_features(ctx, run, i), "seg"); + + /* some kind of feature enable? h26{2,5} has 3 instread */ + push(0x20000, "unk_const"); + push(INST_DMA2, "cm3_dma_config_2"); + push(INST_DMA1, "cm3_dma_config_3"); + + pusha(vp9_ctx->bufs.counts.addr, "frame_counts_addr", 0); + pusha(vp9_ctx->bufs.probs.addr, "hdr_104_probs_addr_lsb8", 0); + + /* always used */ + pusha(vp9_ctx->bufs.state.addr, "hdr_118_pps0_tile_addr_lsb8", 0); + + /* read / write segment buffers */ + pusha(vp9_ctx->bufs.seg.addr, "hdr_108_pps1_tile_addr_lsb8", 1); + pusha(vp9_ctx->bufs.seg.addr, "hdr_108_pps1_tile_addr_lsb8", 2); + /* ping pong buffers, not on intra frames (how apple uses them) */ + pusha(vp9_ctx->bufs.above_info.addr, "hdr_110_pps2_tile_addr_lsb8", 3); + pusha(vp9_ctx->bufs.above_info.addr, "hdr_110_pps2_tile_addr_lsb8", 4); + + push(frame->quant.base_q_idx << 15 + | (frame->quant.delta_q_y_dc & 0x1f) << 10 + | (frame->quant.delta_q_uv_dc & 0x1f) << 5 + | (frame->quant.delta_q_uv_ac & 0x1f) + , "hdr_4c_base_q_idx"); + /* filter related flags? */ + push(frame->lf.sharpness << 28 + | (frame->lf.flags & V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED ? + (frame->lf.ref_deltas[0] & 0x7f) << 21 + | (frame->lf.ref_deltas[1] & 0x7f) << 14 + | (frame->lf.ref_deltas[2] & 0x7f) << 7 + | (frame->lf.ref_deltas[3] & 0x7f) + : 0), "hdr_44_flags1_pt2"); + + push(frame->lf.level << 14 + | (frame->lf.flags & V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED ? + (frame->lf.mode_deltas[0] & 0x7f) << 7 + | (frame->lf.mode_deltas[1] & 0x7f) + : 0), + "hdr_48_loop_filter_level"); + + push(INST_DMA2, "cm3_dma_config_4"); + push(INST_DMA2, "cm3_dma_config_5"); + + if (avd->variant->revision == 3) + push(0, ""); + if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) + pusha(vp9_ctx->bufs.pipe_state.addr, "pipe_state", 0); + + pusha(vp9_ctx->bufs.color[0].addr, "hdr_e8_sps0_tile_addr_lsb8", i); + pusha(vp9_ctx->bufs.color[1].addr, "hdr_e8_sps0_tile_addr_lsb8", i); + + pusha((u64)0, "hdr_e8_sps0_tile_addr_lsb8", i); + + /* not fatal */ + pusha(vp9_ctx->bufs.tiles[0].addr, "hdr_e8_sps0_tile_addr_lsb8", i); + pusha(vp9_ctx->bufs.tiles[1].addr, "hdr_e8_sps0_tile_addr_lsb8", i); + /* fatal if missing / wrong */ + pusha(vp9_ctx->bufs.tiles[2].addr, "hdr_e8_sps0_tile_addr_lsb8", i); + + push(INST_DMA3, "cm3_dma_config_7"); + + push_rvra(avd, ctx, run->addresses.rvra, ctx->rvra.offsets); + + /* confusing */ + pusha((u64)0, "hdr_f4_sps1_tile_addr_lsb8", 2); + + bytesperline = ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline; + if (avd->variant->quirks & AVD_QUIRK_LSR) + bytesperline = bytesperline >> 4; + + pusha(run->addresses.y, "hdr_168_y_addr_lsb8", 0); + push(bytesperline, "hdr_170_width_align"); + pusha(run->addresses.uv, "hdr_16c_uv_addr_lsb8", 0); + push(bytesperline, "hdr_174_width_align"); + push(0, ""); + push(((frame->frame_height_minus_1) << 16) | (frame->frame_width_minus_1), + "cm3_height_width"); + + if (!(intra_only)) + set_refs(ctx, run); +} + + +static void set_tiles(struct avd_ctx *ctx, struct avd_vp9_run *run) +{ + const struct v4l2_ctrl_vp9_frame *frame = run->decode_params; + struct avd_dev *avd = ctx->dev; + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + const u8 *data = vb2_plane_vaddr(&src->vb2_buf, 0); + bool is_last; + + u32 offset = + frame->uncompressed_header_size + frame->compressed_header_size; + u32 size = vb2_get_plane_payload(&src->vb2_buf, 0) - offset; + u32 num_tile_rows = 1 << frame->tile_rows_log2; + u32 num_tile_cols = 1 << frame->tile_cols_log2; + u32 tile_size; + + /* 6.2.6 Compute image size syntax */ + u32 sb_64_cols = (((frame->frame_width_minus_1 + 8) >> 3) + 7) >> 3; + u32 sb_64_rows = (((frame->frame_height_minus_1 + 8) >> 3) + 7) >> 3; + + for (int row = 0; row < num_tile_rows; row++) + for (int col = 0; col < num_tile_cols; col++) { + is_last = row == num_tile_rows - 1 && col == num_tile_cols - 1; + if (row == num_tile_rows - 1 && col == num_tile_cols - 1) { + tile_size = size; + } else { + tile_size = get_unaligned_be32(&data[offset]); + /* i have crashed my computer because of this */ + if (tile_size > size - 4) + return; + offset += 4; + size -= 4; + } + push(0x2d800000 | (u32)(run->addresses.sl >> 32), + "cm3_cmd_set_slice_data"); + push((u32)((run->addresses.sl + offset) & 0xffffffff), + "til_ab4_tile_addr_low"); + push(tile_size, "til_ab8_tile_size"); + push(0x2a000000 + | ((row * sb_64_rows) / num_tile_rows) << 12 + | (col * sb_64_cols) / num_tile_cols, "i"); + + push(col << 24 + | (((row + 1) * sb_64_rows) / num_tile_rows - 1) << 12 + | (((col + 1) * sb_64_cols) / num_tile_cols - 1) + , "til_ac0_tile_dims"); + push(0x2b000000 + | (is_last ? 0x400 : 0) + | (avd->variant->revision == 3 ? 0xfff000 : 0) + , "cm3_cmd_inst_fifo_end"); + offset += tile_size; + size -= tile_size; + vp9_ctx->submit_num++; + } +} + + +static void update_dec_buf_info(struct avd_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + buf->vp9.width = dec_params->frame_width_minus_1 + 1; + buf->vp9.height = dec_params->frame_height_minus_1 + 1; + buf->vp9.bit_depth = dec_params->bit_depth; +} + +static void update_ctx_cur_info(struct avd_vp9_ctx *vp9_ctx, + struct avd_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + vp9_ctx->cur.valid = true; + vp9_ctx->cur.reference_mode = dec_params->reference_mode; + vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; + vp9_ctx->cur.flags = dec_params->flags; + vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; + vp9_ctx->cur.seg = dec_params->seg; + vp9_ctx->cur.lf = dec_params->lf; +} + +static void update_ctx_last_info(struct avd_vp9_ctx *vp9_ctx) +{ + vp9_ctx->last = vp9_ctx->cur; +} + +static void copy_vp9_frame_mv(struct avd_vp9_probs *avd_probs, + const struct v4l2_vp9_frame_context *probs) +{ + memcpy(avd_probs->joint, probs->mv.joint, sizeof(avd_probs->joint)); + for (int i = 0; i < 2; i++) { + avd_probs->mv_comp[i].sign = probs->mv.sign[i]; + memcpy(avd_probs->mv_comp[i].bits, probs->mv.bits[i], + sizeof(avd_probs->mv_comp[i].bits)); + avd_probs->mv_comp[i].class0_bit = probs->mv.class0_bit[i]; + memcpy(avd_probs->mv_comp[i].classes, probs->mv.classes[i], + sizeof(avd_probs->mv_comp[i].bits)); + + memcpy(avd_probs->mv_fr[i].class0_fr, probs->mv.class0_fr[i], + sizeof(avd_probs->mv_fr[i].class0_fr)); + memcpy(avd_probs->mv_fr[i].fr, probs->mv.fr[i], + sizeof(avd_probs->mv_fr[i].fr)); + + avd_probs->mv_hp[i].class0_hp = probs->mv.class0_hp[i]; + avd_probs->mv_hp[i].hp = probs->mv.hp[i]; + } +} + +static void init_probs(struct avd_ctx *ctx, + const struct avd_vp9_run *run) +{ + const struct v4l2_ctrl_vp9_frame *dec_params; + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct avd_vp9_probs *avd_probs = vp9_ctx->bufs.probs.cpu; + const struct v4l2_vp9_segmentation *seg; + const struct v4l2_vp9_frame_context *probs; + bool intra_only; + int count = 0; + + dec_params = run->decode_params; + probs = &vp9_ctx->probability_tables; + seg = &dec_params->seg; + + memset(avd_probs, 0, sizeof(*avd_probs)); + + intra_only = !!(dec_params->flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); + + memcpy(avd_probs->tx8, probs->tx8, sizeof(avd_probs->tx8)); + memcpy(avd_probs->tx16, probs->tx16, sizeof(avd_probs->tx16)); + memcpy(avd_probs->tx32, probs->tx32, sizeof(avd_probs->tx32)); + memcpy(avd_probs->skip, probs->skip, sizeof(avd_probs->skip)); + memcpy(avd_probs->inter_mode, probs->inter_mode, + sizeof(avd_probs->inter_mode)); + memcpy(avd_probs->interp_filter, probs->interp_filter, + sizeof(avd_probs->interp_filter)); + memcpy(avd_probs->is_inter, probs->is_inter, sizeof(avd_probs->is_inter)); + memcpy(avd_probs->comp_mode, probs->comp_mode, + sizeof(avd_probs->comp_mode)); + memcpy(avd_probs->single_ref, probs->single_ref, + sizeof(avd_probs->single_ref)); + memcpy(avd_probs->comp_ref, probs->comp_ref, sizeof(avd_probs->comp_ref)); + memcpy(avd_probs->y_mode, probs->y_mode, sizeof(avd_probs->y_mode)); + + memcpy(avd_probs->partition, + intra_only ? v4l2_vp9_kf_partition_probs : probs->partition, + sizeof(avd_probs->partition)); + memcpy(avd_probs->uv_mode, + intra_only ? v4l2_vp9_kf_uv_mode_prob : probs->uv_mode, + sizeof(avd_probs->uv_mode)); + + copy_vp9_frame_mv(avd_probs, probs); + + /* please mister gcc optimise this away */ + for (int t = 0; t < 4; t++) + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) + for (int k = 0; k < 6; k++) { + int max_l = (k == 0) ? 3 : 6; + for (int l = 0; l < max_l; l++) { + for (int n = 0; n < 3; n++) + avd_probs->coef[count++] = + probs->coef[t][i][j][k][l][n]; + } + } + + memcpy(avd_probs->seg.pred_probs, seg->pred_probs, + sizeof(avd_probs->seg.pred_probs)); + memcpy(avd_probs->seg.tree_probs, seg->tree_probs, + sizeof(avd_probs->seg.tree_probs)); +} + +static int validate_dec_params(struct avd_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + unsigned int aligned_width, aligned_height; + + if (dec_params->frame_height_minus_1 + 1 < 64 || + dec_params->frame_width_minus_1 + 1 < 64) + return -EINVAL; + + aligned_width = round_up(dec_params->frame_width_minus_1 + 1, 64); + aligned_height = round_up(dec_params->frame_height_minus_1 + 1, 16); + + /* + * Userspace should update the capture/decoded format when the + * resolution changes. + */ + if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width || + aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) { + dev_err(ctx->dev->dev, "unexpected bitstream resolution %dx%d\n", + aligned_width, aligned_height); + return -EINVAL; + } + + return 0; +} + +static int avd_vp9_alloc_bufs(struct avd_ctx *ctx) +{ + struct avd_dev *avd = ctx->dev; + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + int ret, size; + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.inst, fifo_size()); + if (ret) + return ret; + + if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) { + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.pipe_state, 0x200); + if (ret) + return ret; + } + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.seg, 0x200); + if (ret) + return ret; + + /* scratch buffer? */ + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.tiles[1], 0x200); + if (ret) + return ret; + + /* TODO */ + size = (((ctx->decoded_fmt.fmt.pix_mp.height - 1) + * (ctx->decoded_fmt.fmt.pix_mp.height - 1) / 0x10000) + 2) * 0x4000; + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.tiles[0], size); + if (ret) + return ret; + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.tiles[2], size); + if (ret) + return ret; + + + for (int i = 0; i < 2; i++) { + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.color[i], size); + if (ret) + return ret; + } + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.above_info, size + 0xc000); + if (ret) + return ret; + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.state, size); + if (ret) + return ret; + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.probs, + sizeof(struct avd_vp9_probs)); + if (ret) + return ret; + + ret = avd_buf_alloc(avd, &vp9_ctx->bufs.counts, + sizeof(struct avd_vp9_frame_symbol_counts)); + if (ret) + return ret; + + return 0; +} + +static int avd_vp9_run_preamble(struct avd_ctx *ctx, + struct avd_vp9_run *run) +{ + struct v4l2_ctrl *ctrl; + const struct v4l2_ctrl_vp9_frame *dec_params; + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + unsigned int fctx_idx; + u32 dst_len; + int ret; + + avd_run_preamble(ctx, &run->base); + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); + if (WARN_ON(!ctrl)) + return -EINVAL; + run->prob_updates = ctrl->p_cur.p; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_VP9_FRAME); + if (WARN_ON(!ctrl)) + return -EINVAL; + dec_params = ctrl->p_cur.p; + + ret = validate_dec_params(ctx, dec_params); + if (ret) + return ret; + + run->decode_params = dec_params; + + vp9_ctx->cur.tx_mode = run->prob_updates->tx_mode; + + fctx_idx = v4l2_vp9_reset_frame_ctx(dec_params, vp9_ctx->frame_context); + vp9_ctx->cur.frame_context_idx = fctx_idx; + + vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; + v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, run->prob_updates, + dec_params); + + run->addresses.sl = + vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0); + + dst_len = run->base.bufs.dst->vb2_buf.planes[0].length; + + run->addresses.y = + vb2_dma_contig_plane_dma_addr(&run->base.bufs.dst->vb2_buf, 0); + + run->addresses.uv = run->addresses.y + + ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline * + ALIGN(ctx->decoded_fmt.fmt.pix_mp.height, 16); + + run->addresses.rvra = run->addresses.y + (dst_len - ctx->rvra.size); + + return 0; +} + +static int avd_vp9_run(struct avd_ctx *ctx) +{ + struct avd_dev *avd = ctx->dev; + struct avd_vp9_run run; + struct avd_vp9_ctx *vp9_ctx; + struct avd_decoded_buffer *dst; + int ret; + + ret = avd_vp9_run_preamble(ctx, &run); + if (ret) { + avd_run_postamble(ctx, &run.base); + return ret; + } + + init_probs(ctx, &run); + + vp9_ctx = ctx->priv; + dst = vb2_to_avd_decoded_buf(&run.base.bufs.dst->vb2_buf); + update_dec_buf_info(dst, run.decode_params); + update_ctx_cur_info(vp9_ctx, dst, run.decode_params); + + schedule_delayed_work(&ctx->watchdog_work, msecs_to_jiffies(2000)); + + ret = alloc_slots(avd, ctx, AVD_CODEC_VP9); + if (ret) { + dev_err(avd->dev, "no free slots: %d", ret); + return ret; + } + + avd->variant->configure_stream(avd, vp9_ctx->bufs.inst.addr, + ctx->fifo_idx, ctx->vp_slot); + + set_header(ctx, &run); + + vp9_ctx->submit_num = 0; + set_tiles(ctx, &run); + avd_run_postamble(ctx, &run.base); + + return 0; +} + +#define copy_tx_and_skip(p1, p2) \ +do { \ + memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ + memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ + memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ + memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ +} while (0) + + +static void avd_vp9_done(struct avd_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + struct vb2_v4l2_buffer *dst_buf, + enum vb2_buffer_state result) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct avd_vp9_frame_symbol_counts *c = vp9_ctx->bufs.counts.cpu; + unsigned int fctx_idx; + + /* v4l2-specific stuff */ + if (result == VB2_BUF_STATE_ERROR) + goto out_update_last; + + /* + * vp9 stuff + * + * 6.1.2 refresh_probs() + * + * In the spec a complementary condition goes last in 6.1.2 refresh_probs(), + * but it makes no sense to perform all the activities from the first "if" + * there if we actually are not refreshing the frame context. On top of that, + * because of 6.2 uncompressed_header() whenever error_resilient_mode == 1, + * refresh_frame_context == 0. Consequently, if we don't jump to out_update_last + * it means error_resilient_mode must be 0. + */ + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) + goto out_update_last; + + fctx_idx = vp9_ctx->cur.frame_context_idx; + + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { + /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ + struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; + bool frame_is_intra = vp9_ctx->cur.flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); + struct tx_and_skip { + u8 tx8[2][1]; + u8 tx16[2][2]; + u8 tx32[2][3]; + u8 skip[3]; + } _tx_skip, *tx_skip = &_tx_skip; + struct v4l2_vp9_frame_symbol_counts *counts; + + /* buffer the forward-updated TX and skip probs */ + if (frame_is_intra) + copy_tx_and_skip(tx_skip, probs); + + /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ + *probs = vp9_ctx->frame_context[fctx_idx]; + + /* if FrameIsIntra then undo the effect of load_probs2() */ + if (frame_is_intra) + copy_tx_and_skip(probs, tx_skip); + + counts = &vp9_ctx->cnts; + + v4l2_vp9_adapt_coef_probs(probs, counts, + !vp9_ctx->last.valid || + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, + frame_is_intra); + if (!frame_is_intra) { + const struct avd_vp9_frame_symbol_counts *cnts; + int i; + u32 tx16p[2][4]; + u32 sign[2][2]; + u32 classes[2][11]; + u32 class0[2][2]; + u32 bits[2][10][2]; + u32 class0_fp[2][2][4]; + u32 fp[2][4]; + u32 class0_hp[2][2]; + u32 hp[2][2]; + + cnts = vp9_ctx->bufs.counts.cpu; + + for (i = 0; i < ARRAY_SIZE(cnts->tx16p); ++i) + memcpy(tx16p[i], cnts->tx16p[i], sizeof(tx16p[0])); + + for (i = 0; i < 2; i++) { + memcpy(sign[i], cnts->mv_comp[i].sign, sizeof(sign[0])); + memcpy(classes[i], cnts->mv_comp[i].classes, sizeof(classes[0])); + memcpy(class0[i], cnts->mv_comp[i].class0, sizeof(class0[0])); + memcpy(bits[i], cnts->mv_comp[i].bits, sizeof(bits[0])); + memcpy(class0_fp[i], cnts->mv_fr[i].class0_fr, + sizeof(class0_fp[0])); + memcpy(fp[i], cnts->mv_fr[i].fr, sizeof(fp[0])); + memcpy(class0_hp[i], cnts->mv_hp[i].class0_hp, + sizeof(class0_hp[0])); + memcpy(hp[i], cnts->mv_hp[i].hp, sizeof(hp[0])); + } + + counts->tx16p = &tx16p; + counts->sign = &sign; + counts->classes = &classes; + counts->class0 = &class0; + counts->bits = &bits; + counts->class0_fp = &class0_fp; + counts->fp = &fp; + counts->class0_hp = &class0_hp; + counts->hp = &hp; + + /* load_probs2() already done */ + v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, + vp9_ctx->cur.reference_mode, + vp9_ctx->cur.interpolation_filter, + vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); + } + } + + /* 6.1.2 refresh_probs(): save_probs(fctx_idx) */ + vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; + +out_update_last: + update_ctx_last_info(vp9_ctx); +} + +static noinline_for_stack void +avd_init_v4l2_vp9_count_tbl(struct avd_ctx *ctx) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct avd_vp9_frame_symbol_counts *cnts = vp9_ctx->bufs.counts.cpu; + int coeff_cnts = 0, eob_cnts = 0; + + vp9_ctx->cnts.intra_inter = &cnts->intra_inter; + vp9_ctx->cnts.y_mode = &cnts->y_mode; + vp9_ctx->cnts.uv_mode = &cnts->uv_mode; + vp9_ctx->cnts.comp = &cnts->comp; + vp9_ctx->cnts.comp_ref = &cnts->comp_ref; + vp9_ctx->cnts.single_ref = &cnts->single_ref; + vp9_ctx->cnts.filter = &cnts->filter; + vp9_ctx->cnts.mv_mode = &cnts->mv_mode; + vp9_ctx->cnts.mv_joint = &cnts->mv_joint; + + /* all of the mv use a different structure, so they must all be copied */ + + vp9_ctx->cnts.tx8p = &cnts->tx8p; + /* avd also uses "u32 tx16p[2][3]" instead of "u32 tx16p[2][4]" */ + vp9_ctx->cnts.tx32p = &cnts->tx32p; + + /* it would suck if we need to reverse partition */ + vp9_ctx->cnts.partition = &cnts->partition; + /* these should work */ + vp9_ctx->cnts.skip = &cnts->skip; + + for (int t = 0; t < 4; t++) + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) + for (int k = 0; k < 6; k++) { + int max_l = (k == 0) ? 3 : 6; + for (int l = 0; l < max_l; l++) { + vp9_ctx->cnts.coeff[t][i][j][k][l] = + (u32 (*)[3])&cnts->ref_cnt[coeff_cnts]; + coeff_cnts += 3; + vp9_ctx->cnts.eob[t][i][j][k][l][0] = + &cnts->eob_0[eob_cnts++]; + vp9_ctx->cnts.eob[t][i][j][k][l][1] = + &cnts->ref_cnt[coeff_cnts++]; + } + } +} + +static int avd_vp9_start(struct avd_ctx *ctx) +{ + struct avd_vp9_ctx *vp9_ctx; + int ret; + + vp9_ctx = kzalloc(sizeof(*vp9_ctx), GFP_KERNEL); + if (!vp9_ctx) + return -ENOMEM; + + ctx->priv = vp9_ctx; + ret = avd_vp9_alloc_bufs(ctx); + if (ret) + goto err_free_ctx; + + avd_init_v4l2_vp9_count_tbl(ctx); + + return 0; + +err_free_ctx: + kfree(vp9_ctx); + ctx->priv = NULL; + return ret; +} + +static void avd_vp9_stop(struct avd_ctx *ctx) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct avd_dev *avd = ctx->dev; + + avd_buf_free(avd, &vp9_ctx->bufs.pipe_state); + avd_buf_free(avd, &vp9_ctx->bufs.inst); + avd_buf_free(avd, &vp9_ctx->bufs.probs); + avd_buf_free(avd, &vp9_ctx->bufs.counts); + avd_buf_free(avd, &vp9_ctx->bufs.seg); + avd_buf_free(avd, &vp9_ctx->bufs.above_info); + avd_buf_free(avd, &vp9_ctx->bufs.color[0]); + avd_buf_free(avd, &vp9_ctx->bufs.color[1]); + avd_buf_free(avd, &vp9_ctx->bufs.state); + for (int i = 0; i < 3; i++) + avd_buf_free(avd, &vp9_ctx->bufs.tiles[i]); + + kfree(vp9_ctx); +} + +static enum avd_image_fmt avd_vp9_get_image_fmt(struct avd_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ +#define BIT_DEPTH(chroma) \ + (frame->bit_depth == 8 ? AVD_IMG_FMT_##chroma##_8BIT : \ + AVD_IMG_FMT_##chroma##_10BIT ) + const struct v4l2_ctrl_vp9_frame *frame = ctrl->p_new.p_vp9_frame; + + if (ctrl->id != V4L2_CID_STATELESS_VP9_FRAME) + return AVD_IMG_FMT_ANY; + + /* 7.2.2 Color config semantics */ + if (frame->flags & V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING) { + if (frame->flags & V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING) + return BIT_DEPTH(420); + else + return BIT_DEPTH(422); + } + + return AVD_IMG_FMT_ANY; +#undef BIT_DEPTH +} + +static void avd_vp9_submit(struct avd_ctx *ctx) +{ + struct avd_vp9_ctx *vp9_ctx = ctx->priv; + struct avd_dev *avd = ctx->dev; + u32 submit_mask = ctx->dev->variant->revision == 3 ? 0xfff000 : 0; + + writel(0x2b000000 + | submit_mask + | (avd->variant->revision == 3 ? 0x100 : 0x200) + | (ctx->fifo_idx << 4) + | avd->variant->fifo_slots, + avd->ctrl + avd->variant->submit_offset); + for (int i = 0; i < vp9_ctx->submit_num - 1; i++) + writel(0x2b000000 + | submit_mask + | (ctx->fifo_idx << 4) + | avd->variant->fifo_slots, + avd->ctrl + avd->variant->submit_offset); +} + +const struct avd_coded_fmt_ops avd_vp9_fmt_ops = { + .start = avd_vp9_start, + .stop = avd_vp9_stop, + .run = avd_vp9_run, + .done = avd_vp9_done, + .submit = avd_vp9_submit, + .get_image_fmt = avd_vp9_get_image_fmt, +}; diff --git a/drivers/media/platform/apple/avd/avd.h b/drivers/media/platform/apple/avd/avd.h new file mode 100644 index 00000000000000..a736eaa2cc5213 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd.h @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple Video Decoder driver + * + * Copyright The Asahi Linux Contributors + * + * Based on rkvdec driver by Collabora, Ltd. + * Copyright (C) 2019 Collabora, Ltd. + * Based on rkvdec driver by Google LLC. (Tomasz Figa ) + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#ifndef AVD_H_ +#define AVD_H_ + +#include "linux/bitmap.h" +#include +#include +#include + +#include +#include +#include +#include + +#define AVD_CAPABILITY_HEVC BIT(0) +#define AVD_CAPABILITY_H264 BIT(1) +#define AVD_CAPABILITY_VP9 BIT(2) + +/* Shifts addreses right and bytesperline?? */ +#define AVD_QUIRK_LSR BIT(0) +#define AVD_QUIRK_NO_PIPE_STATE BIT(1) + +#define VP_SLOT_NONE 255 +#define INST_FIFO_SLOT_NONE 255 + + +struct avd_ctx; +struct avd_dev; + +/* Matches hdr mode (inst stream) and register layout */ +enum avd_codec { + AVD_CODEC_HEVC = 0, + AVD_CODEC_H264 = 1, + AVD_CODEC_VP9 = 2, + AVD_CODEC_AV1 = 3 +}; + +struct avd_run { + struct { + struct vb2_v4l2_buffer *src; /* OUTPUT coded */ + struct vb2_v4l2_buffer *dst; /* CAPTURE decoded */ + } bufs; +}; + +struct avd_ctrl_desc { + struct v4l2_ctrl_config cfg; +}; + +struct avd_ctrls { + const struct avd_ctrl_desc *ctrls; + unsigned int num_ctrls; +}; + +struct avd_vp9_decoded_buffer_info { + /* Info needed when the decoded frame serves as a reference frame. */ + unsigned short width; + unsigned short height; + unsigned int bit_depth : 4; +}; + +struct avd_rvra { + u32 offsets[4]; /* sizes or offsets */ + u32 size; +}; + +/* TODO: change and use this */ +struct avd_decoded_buffer { + /* Must be the first field in this struct. */ + struct v4l2_m2m_buffer base; + + struct avd_rvra rvra; + + union { + struct avd_vp9_decoded_buffer_info vp9; + }; + +}; + +static inline struct avd_decoded_buffer * +vb2_to_avd_decoded_buf(struct vb2_buffer *buf) +{ + return container_of(buf, struct avd_decoded_buffer, base.vb.vb2_buf); +} + +struct avd_coded_fmt_ops { + void (*adjust_decoded_fmt)(struct avd_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp); + void (*submit)(struct avd_ctx *ctx); + int (*start)(struct avd_ctx *ctx); + void (*stop)(struct avd_ctx *ctx); + int (*run)(struct avd_ctx *ctx); + void (*done)(struct avd_ctx *ctx, struct vb2_v4l2_buffer *src_buf, + struct vb2_v4l2_buffer *dst_buf, + enum vb2_buffer_state result); + int (*try_ctrl)(struct avd_ctx *ctx, struct v4l2_ctrl *ctrl); + enum avd_image_fmt (*get_image_fmt)(struct avd_ctx *ctx, + struct v4l2_ctrl *ctrl); +}; + +enum avd_image_fmt { + AVD_IMG_FMT_ANY = 0, + AVD_IMG_FMT_420_8BIT, + AVD_IMG_FMT_420_10BIT, + AVD_IMG_FMT_422_8BIT, + AVD_IMG_FMT_422_10BIT, +}; + +struct avd_decoded_fmt_desc { + u32 fourcc; + enum avd_image_fmt image_fmt; +}; + +struct avd_coded_fmt_desc { + u32 fourcc; + struct v4l2_frmsize_stepwise frmsize; + const struct avd_ctrls *ctrls; + const struct avd_coded_fmt_ops *ops; + unsigned int num_decoded_fmts; + const struct avd_decoded_fmt_desc *decoded_fmts; + u32 subsystem_flags; + unsigned int capability; +}; + +struct avd_variant { + unsigned int vp_slots[4]; + unsigned int fifo_slots; + unsigned int capabilities; + void (*configure_stream)(struct avd_dev *avd, dma_addr_t addr, + u8 fifo_idx, u32 vp_slot); + const char* fw_name; + unsigned char revision; /* the same as the device tree */ + /* just for convenience */ + u32 vp_slot_offset; + u32 submit_offset; + unsigned int quirks; +}; + +struct avd_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct media_device mdev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct platform_device *pdev; + + const struct firmware *fw; /* fw is lost on suspend */ + + void __iomem *code; + void __iomem *mbox; + void __iomem *ctrl; + + struct iommu_domain *domain; + struct iommu_domain *empty_domain; + + struct mutex vdev_lock; + + struct reset_control *rstc; + + unsigned long vp_slots; + unsigned long inst_fifo_slots; + + const struct avd_variant *variant; +}; + +struct avd_ctx { + struct v4l2_fh fh; + struct avd_dev *dev; + + struct v4l2_format coded_fmt; + struct v4l2_format decoded_fmt; + + const struct avd_coded_fmt_desc *coded_fmt_desc; + struct v4l2_ctrl_handler ctrl_hdl; + enum avd_image_fmt image_fmt; + + struct delayed_work watchdog_work; + + void *priv; + + /* reference VRA (video resolution adaptation) scaler buffer. */ + struct avd_rvra rvra; + + u8 fifo_idx; + u8 vp_slot; +}; + +struct avd_buf { + void *cpu; + dma_addr_t addr; + size_t size; +}; + +int avd_buf_alloc(struct avd_dev *avd, struct avd_buf *buf, size_t size); +void avd_buf_free(struct avd_dev *avd, struct avd_buf *buf); + +void avd_reset_coded_fmt(struct avd_ctx *ctx); +void avd_reset_decoded_fmt(struct avd_ctx *ctx); +int avd_init_ctrls(struct avd_ctx *ctx); + +void avd_job_finish_no_pm(struct avd_ctx *ctx, enum vb2_buffer_state result); +void avd_job_finish(struct avd_ctx *ctx, enum vb2_buffer_state result); + +void avd_run_preamble(struct avd_ctx *ctx, struct avd_run *run); +void avd_run_postamble(struct avd_ctx *ctx, struct avd_run *run); + +extern const struct avd_coded_fmt_ops avd_h264_fmt_ops; +extern const struct avd_coded_fmt_ops avd_hevc_fmt_ops; +extern const struct avd_coded_fmt_ops avd_vp9_fmt_ops; + +extern const struct v4l2_ctrl_ops avd_ctrl_ops; +extern const struct v4l2_ioctl_ops avd_ioctl_ops; +extern const struct vb2_ops avd_queue_ops; + +static inline u32 fmt_height(struct avd_ctx *ctx) +{ + return ctx->coded_fmt.fmt.pix_mp.height; +} +static inline u32 fmt_width(struct avd_ctx *ctx) +{ + return ctx->coded_fmt.fmt.pix_mp.width; +} + +void fill_rvra(struct avd_rvra *rvra, enum avd_image_fmt image_fmt, + u32 width, u32 height); +int alloc_slots(struct avd_dev *avd, struct avd_ctx *ctx, enum avd_codec codec); + +static inline void free_vp_slot(struct avd_dev *avd, struct avd_ctx *ctx) +{ + clear_bit(ctx->vp_slot, &avd->vp_slots); + ctx->vp_slot = VP_SLOT_NONE; +} + +static inline void free_inst_slot(struct avd_dev *avd, struct avd_ctx *ctx) +{ + clear_bit(ctx->fifo_idx, &avd->inst_fifo_slots); + ctx->fifo_idx = INST_FIFO_SLOT_NONE; +} + +static inline struct avd_ctx *file_to_ctx(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct avd_ctx, fh); +} + +/* hw stuff */ +int avd_boot(struct avd_dev *avd); +void avd_shutdown(struct avd_dev *avd); + +void avd_status(struct avd_dev *avd, u32 vp); + +void t8103_configure_stream(struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot); +void t8112_configure_stream(struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot); +void t8122_configure_stream (struct avd_dev *avd, dma_addr_t addr, u8 fifo_idx, + u32 vp_slot); + +#endif /* AVD_H_ */