On 19/03/2024 13:06, Naushir Patuck wrote:
> Hi,
>
> On Tue, 19 Mar 2024 at 09:32, Krzysztof Kozlowski
> <krzysztof.kozlowski@linaro.org> wrote:
>>
>> On 19/03/2024 08:00, Tomi Valkeinen wrote:
>>> On 19/03/2024 08:48, Tomi Valkeinen wrote:
>>>> On 19/03/2024 08:23, Krzysztof Kozlowski wrote:
>>>>> On 18/03/2024 16:49, Tomi Valkeinen wrote:
>>>>>> Add DT bindings for raspberrypi,rp1-cfe.
>>>>>>
>>>>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>>>>>> ---
>>>>>> .../bindings/media/raspberrypi,rp1-cfe.yaml | 103
>>>>>> +++++++++++++++++++++
>>>>>> 1 file changed, 103 insertions(+)
>>>>>>
>>>>>> diff --git
>>>>>> a/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>>>> b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>>>> new file mode 100644
>>>>>> index 000000000000..7b2beeaaab0e
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>>>> @@ -0,0 +1,103 @@
>>>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>>>> +%YAML 1.2
>>>>>> +---
>>>>>> +$id: http://devicetree.org/schemas/media/raspberrypi,rp1-cfe.yaml#
>>>>>
>>>>> Use compatible as filename.
>>>>
>>>> Ah, indeed. I changed the compatible quite late, adding the "rpi5" as
>>>> versioning, and missed changing the file name.
>>>>
>>>> I'll rename.
>>>
>>> Actually, maybe it's better to have two compatibles,
>>> "raspberrypi,rp1-cfe" as the generic one, and "raspberrypi,rpi5-rp1-cfe"
>>> (or something similar) for RaspberryPi 5.
>>>
>>> And I'm not sure if the "rp1" part is relevant there, would
>>> "raspberrypi,cfe" be just as fine? Naush?
>>
>> See writing bindings. Compatibles should be SoC specific. In some cases
>> generic fallbacks make sense, in some note. But don't just choose
>> "generic fallback" because you want. Provide rationale.
>
> If the compatible is SoC specific, I suppose "raspberrypi,rp1-cfe"
> would be the correct string.
Sure, but then please think what if rp1 is on Rpi6, called exactly the
same (rp1), with some minor differences? Could it be? I don't know, you
are upstreaming this stuff. Just be also sure you read writing bindings
document.
Best regards,
Krzysztof
Hi, On Tue, 19 Mar 2024 at 09:32, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote: > > On 19/03/2024 08:00, Tomi Valkeinen wrote: > > On 19/03/2024 08:48, Tomi Valkeinen wrote: > >> On 19/03/2024 08:23, Krzysztof Kozlowski wrote: > >>> On 18/03/2024 16:49, Tomi Valkeinen wrote: > >>>> Add DT bindings for raspberrypi,rp1-cfe. > >>>> > >>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > >>>> --- > >>>> .../bindings/media/raspberrypi,rp1-cfe.yaml | 103 > >>>> +++++++++++++++++++++ > >>>> 1 file changed, 103 insertions(+) > >>>> > >>>> diff --git > >>>> a/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml > >>>> b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml > >>>> new file mode 100644 > >>>> index 000000000000..7b2beeaaab0e > >>>> --- /dev/null > >>>> +++ b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml > >>>> @@ -0,0 +1,103 @@ > >>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > >>>> +%YAML 1.2 > >>>> +--- > >>>> +$id: http://devicetree.org/schemas/media/raspberrypi,rp1-cfe.yaml# > >>> > >>> Use compatible as filename. > >> > >> Ah, indeed. I changed the compatible quite late, adding the "rpi5" as > >> versioning, and missed changing the file name. > >> > >> I'll rename. > > > > Actually, maybe it's better to have two compatibles, > > "raspberrypi,rp1-cfe" as the generic one, and "raspberrypi,rpi5-rp1-cfe" > > (or something similar) for RaspberryPi 5. > > > > And I'm not sure if the "rp1" part is relevant there, would > > "raspberrypi,cfe" be just as fine? Naush? > > See writing bindings. Compatibles should be SoC specific. In some cases > generic fallbacks make sense, in some note. But don't just choose > "generic fallback" because you want. Provide rationale. If the compatible is SoC specific, I suppose "raspberrypi,rp1-cfe" would be the correct string. Naush > > Best regards, > Krzysztof >
Merged and pushed out. https://gitlab.com/kernel-firmware/linux-firmware/-/merge_requests/178 josh On Fri, Mar 15, 2024 at 12:16 AM Irui Wang <irui.wang@mediatek.com> wrote: > > [h264 encoder] > improve encoder quality > > [vp9 decoder] > parse intra only frame uncompressed header > > [h264 decoder] > manage decoding dpb list > > Release Version: 1.1.8 > > Signed-off-by: Irui Wang <irui.wang@mediatek.com> > --- > changed with v1: > - fix commit message typo > --- > mediatek/mt8173/vpu_d.bin | Bin 2977184 -> 2977184 bytes > mediatek/mt8173/vpu_p.bin | Bin 131180 -> 131380 bytes > 2 files changed, 0 insertions(+), 0 deletions(-) > > diff --git a/mediatek/mt8173/vpu_d.bin b/mediatek/mt8173/vpu_d.bin > index e966b49bee57e8cfaa3536174830916cf35b184f..06aa3fb83b4e8d9b6921f7429b66c37744aad4a9 100644 > GIT binary patch > delta 550 > zcmajZJxBs!7{Kx8)H^LdDzh)UO6|)m4vLDVAc?@uDWSQksiDCkFbZ<5ZfkNl8X`hG > zQV43wrM0$(8VZ`-gogeXwiUkcyZ_z$ymwsb<whx?>{GS3ipW*P45^8zZi;-K$<Nwi > zJ@2~YoxQSYp9XI=+Y`txxmS#NJGXY{GD;63k-CVE1d_O9&!7vf`(?U{jk=v)ug25a > zR}q!(A5e|PK194UOMT8h_0z>CkpSJMLFc(45^|m!{K-!E$jTS3yaHFv%DcY`1>ID{ > zv5EbbL;4qIevFfU=D+Va^K)FZId^R;Dd8g3HV5ySKNd4hH#|_G!3!Tc&<Q^R2qJ_o > vbfX7h^df>j^kV>n7(x`o&@qBhj3I_N5*Wt>CNYI+Br$^&W^0+|TyF0h_inlQ > > delta 550 > zcmajZy-NaN9KiA4-Fc_wOJ!DOubRD?L<CV&kVqgzG(<v^b5nzZn?r+Jr2PX9{s9M9 > z2~rKYv=(hq1VLjs1wupL2W%^R;B()*=l9%k)#ppC3fp5^EnDQ;J`b3&$WB=#47Cnf > zPsh51Dk5)Xr|fT7j?1^VW~_@i*{vIW*}fBrH${?I#ReMeZPHZdew*&%py{O2SJ71N > zMa1OmXVjuA?;>uRqaL+Sy)^wG;-h)$SI;exfO>B6Cr81PTA@@cXwd7mg7IA}?pHKX > zPI1xEpnp;GTipLM|9wZzAMx7d+($)A2p9S6a`20}Udpr$xM0G98y@ta7hd?_M*w~3 > u#{hyDL<mC|#t6a~MFe9Q#{?!Zg=s_)!wlk>#T@3bfJG#*RL``N+58V|&cV|F > > diff --git a/mediatek/mt8173/vpu_p.bin b/mediatek/mt8173/vpu_p.bin > index 55a49ec244539011213928bb66909a3564af703e..e91505e1194347a47ea9e48afce0ca9a790b37ad 100644 > GIT binary patch > delta 10973 > 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 > LP2R>Es2}|wL!|Yo > > delta 10312 > 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 > OY!32x-jzKHJ@7w;@g&3m > > -- > 2.25.1 >
Hello, Nas On Tue, Mar 19, 2024 at 10:56:22AM +0000, Nas Chung wrote: > Hi, Ivan. > > >-----Original Message----- > >From: Ivan Bornyakov <brnkv.i1@gmail.com> > >Sent: Monday, March 18, 2024 11:42 PM > >To: Nas Chung <nas.chung@chipsnmedia.com>; jackson.lee > ><jackson.lee@chipsnmedia.com>; Mauro Carvalho Chehab <mchehab@kernel.org> > >Cc: Ivan Bornyakov <brnkv.i1@gmail.com>; Philipp Zabel > ><p.zabel@pengutronix.de>; Rob Herring <robh@kernel.org>; Krzysztof > >Kozlowski <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley > ><conor+dt@kernel.org>; linux-media@vger.kernel.org; linux- > >kernel@vger.kernel.org; devicetree@vger.kernel.org > >Subject: [PATCH 5/6] media: chips-media: wave5: refine SRAM usage > > > >Allocate SRAM memory on module probe, free on remove. There is no need > >to allocate on device open, free on close, the memory is the same every > >time. > > If there is no decoder/encoder instance, driver don't need to allocate SRAM memory. > The main reason of allocating the memory in open() is to allow other modules to > use more SRAM memory, if wave5 is not working. > > > > >Also use gen_pool_size() to determine SRAM memory size to be allocated > >instead of separate "sram-size" DT property to reduce duplication. > > > >Signed-off-by: Ivan Bornyakov <brnkv.i1@gmail.com> > >--- > > .../platform/chips-media/wave5/wave5-helper.c | 3 --- > > .../platform/chips-media/wave5/wave5-vdi.c | 21 ++++++++++--------- > > .../chips-media/wave5/wave5-vpu-dec.c | 2 -- > > .../chips-media/wave5/wave5-vpu-enc.c | 2 -- > > .../platform/chips-media/wave5/wave5-vpu.c | 12 +++++------ > > .../platform/chips-media/wave5/wave5-vpuapi.h | 1 - > > 6 files changed, 16 insertions(+), 25 deletions(-) > > > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c > >b/drivers/media/platform/chips-media/wave5/wave5-helper.c > >index 8433ecab230c..ec710b838dfe 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c > >+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c > >@@ -29,9 +29,6 @@ void wave5_cleanup_instance(struct vpu_instance *inst) > > { > > int i; > > > >- if (list_is_singular(&inst->list)) > >- wave5_vdi_free_sram(inst->dev); > >- > > for (i = 0; i < inst->fbc_buf_count; i++) > > wave5_vpu_dec_reset_framebuffer(inst, i); > > > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c > >b/drivers/media/platform/chips-media/wave5/wave5-vdi.c > >index 3809f70bc0b4..ee671f5a2f37 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c > >+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c > >@@ -174,16 +174,19 @@ int wave5_vdi_allocate_array(struct vpu_device > >*vpu_dev, struct vpu_buf *array, > > void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) > > { > > struct vpu_buf *vb = &vpu_dev->sram_buf; > >+ dma_addr_t daddr; > >+ void *vaddr; > >+ size_t size; > > > >- if (!vpu_dev->sram_pool || !vpu_dev->sram_size) > >+ if (!vpu_dev->sram_pool || vb->vaddr) > > return; > > > >- if (!vb->vaddr) { > >- vb->size = vpu_dev->sram_size; > >- vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, > >- &vb->daddr); > >- if (!vb->vaddr) > >- vb->size = 0; > >+ size = gen_pool_size(vpu_dev->sram_pool); > >+ vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr); > >+ if (vaddr) { > >+ vb->vaddr = vaddr; > >+ vb->daddr = daddr; > >+ vb->size = size; > > } > > > > dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: > >0x%p\n", > >@@ -197,9 +200,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev) > > if (!vb->size || !vb->vaddr) > > return; > > > >- if (vb->vaddr) > >- gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, > >- vb->size); > >+ gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb- > >>size); > > > > memset(vb, 0, sizeof(*vb)); > > } > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c > >b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c > >index aa0401f35d32..84dbe56216ad 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c > >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c > >@@ -1854,8 +1854,6 @@ static int wave5_vpu_open_dec(struct file *filp) > > goto cleanup_inst; > > } > > > >- wave5_vdi_allocate_sram(inst->dev); > >- > > return 0; > > > > cleanup_inst: > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c > >b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c > >index 8bbf9d10b467..86ddcb82443b 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c > >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c > >@@ -1727,8 +1727,6 @@ static int wave5_vpu_open_enc(struct file *filp) > > goto cleanup_inst; > > } > > > >- wave5_vdi_allocate_sram(inst->dev); > >- > > return 0; > > > > cleanup_inst: > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c > >b/drivers/media/platform/chips-media/wave5/wave5-vpu.c > >index f3ecadefd37a..2a0a70dd7062 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c > >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c > >@@ -178,16 +178,11 @@ static int wave5_vpu_probe(struct platform_device > >*pdev) > > return ret; > > } > > > >- ret = of_property_read_u32(pdev->dev.of_node, "sram-size", > >- &dev->sram_size); > >- if (ret) { > >- dev_warn(&pdev->dev, "sram-size not found\n"); > >- dev->sram_size = 0; > >- } > >- > > Required SRAM size is different from each wave5 product. > And, SoC vendor also can configure the different SRAM size > depend on target SoC specification even they use the same wave5 product. > One can limit iomem address range in SRAM node. Here is the example of how I setup Wave515 with SRAM: sram@2000000 { compatible = "mmio-sram"; reg = <0x0 0x2000000 0x0 0x80000>; #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x0 0x2000000 0x80000>; wave515_vpu_sram: wave515-vpu-sram@0 { reg = <0x0 0x80000>; pool; }; }; wave515@410000 { compatible = "cnm,wave515"; reg = <0x0 0x410000 0x0 0x10000>; clocks = <&clk_ref1>; clock-names = "videc"; interrupt-parent = <&wave515_intc>; interrupts = <16 IRQ_TYPE_LEVEL_HIGH>; resets = <&wave515_reset 0>, <&wave515_reset 4>, <&wave515_reset 8>, <&wave515_reset 12>; sram = <&wave515_vpu_sram>; }; gen_pool_size() returns size of wave515_vpu_sram, no need for extra "sram-size" property. > Thanks. > Nas. > > > dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); > > if (!dev->sram_pool) > > dev_warn(&pdev->dev, "sram node not found\n"); > >+ else > >+ wave5_vdi_allocate_sram(dev); > > > > dev->product_code = wave5_vdi_read_register(dev, > >VPU_PRODUCT_CODE_REGISTER); > > ret = wave5_vdi_init(&pdev->dev); > >@@ -259,6 +254,8 @@ static int wave5_vpu_probe(struct platform_device > >*pdev) > > err_clk_dis: > > clk_bulk_disable_unprepare(dev->num_clks, dev->clks); > > > >+ wave5_vdi_free_sram(dev); > >+ > > return ret; > > } > > > >@@ -275,6 +272,7 @@ static void wave5_vpu_remove(struct platform_device > >*pdev) > > v4l2_device_unregister(&dev->v4l2_dev); > > wave5_vdi_release(&pdev->dev); > > ida_destroy(&dev->inst_ida); > >+ wave5_vdi_free_sram(dev); > > } > > > > static const struct wave5_match_data ti_wave521c_data = { > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h > >b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h > >index fa62a85080b5..8d88381ac55e 100644 > >--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h > >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h > >@@ -749,7 +749,6 @@ struct vpu_device { > > struct vpu_attr attr; > > struct vpu_buf common_mem; > > u32 last_performance_cycles; > >- u32 sram_size; > > struct gen_pool *sram_pool; > > struct vpu_buf sram_buf; > > void __iomem *vdb_register; > >-- > >2.44.0 >
Il 19/03/24 08:02, Shawn Sung ha scritto: > From: Hsiao Chien Sung <shawn.sung@mediatek.corp-partner.google.com> > > Rename all "mtk_drm_ddp_comp" to "mtk_ddp_comp": > - To align the naming rule > - To reduce the code size > > Reviewed-by: AngeloGiaocchino Del Regno <angelogioacchino.delregno@collabora.com> Shawn, I don't know if I typoed my own name (which is actually possible, since I write the tags by hand), or what actually happened to my Reviewed-by tags on the entire series. Can you please fix the typo in the tag? Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Use this one, please. Thanks, Angelo > Reviewed-by: CK Hu <ck.hu@mediatek.com> > Signed-off-by: Hsiao Chien Sung <shawn.sung@mediatek.corp-partner.google.com>
Il 19/03/24 08:02, Shawn Sung ha scritto:
> From: Hsiao Chien Sung <shawn.sung@mediatek.corp-partner.google.com>
>
> Rename files mtk_drm_plane.c to mtk_plane.c and
> modify the Makefile accordingly.
>
> Signed-off-by: Hsiao Chien Sung <shawn.sung@mediatek.corp-partner.google.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Hi, Ivan. >-----Original Message----- >From: Ivan Bornyakov <brnkv.i1@gmail.com> >Sent: Monday, March 18, 2024 11:42 PM >To: Nas Chung <nas.chung@chipsnmedia.com>; jackson.lee ><jackson.lee@chipsnmedia.com>; Mauro Carvalho Chehab <mchehab@kernel.org> >Cc: Ivan Bornyakov <brnkv.i1@gmail.com>; Philipp Zabel ><p.zabel@pengutronix.de>; Rob Herring <robh@kernel.org>; Krzysztof >Kozlowski <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley ><conor+dt@kernel.org>; linux-media@vger.kernel.org; linux- >kernel@vger.kernel.org; devicetree@vger.kernel.org >Subject: [PATCH 5/6] media: chips-media: wave5: refine SRAM usage > >Allocate SRAM memory on module probe, free on remove. There is no need >to allocate on device open, free on close, the memory is the same every >time. If there is no decoder/encoder instance, driver don't need to allocate SRAM memory. The main reason of allocating the memory in open() is to allow other modules to use more SRAM memory, if wave5 is not working. > >Also use gen_pool_size() to determine SRAM memory size to be allocated >instead of separate "sram-size" DT property to reduce duplication. > >Signed-off-by: Ivan Bornyakov <brnkv.i1@gmail.com> >--- > .../platform/chips-media/wave5/wave5-helper.c | 3 --- > .../platform/chips-media/wave5/wave5-vdi.c | 21 ++++++++++--------- > .../chips-media/wave5/wave5-vpu-dec.c | 2 -- > .../chips-media/wave5/wave5-vpu-enc.c | 2 -- > .../platform/chips-media/wave5/wave5-vpu.c | 12 +++++------ > .../platform/chips-media/wave5/wave5-vpuapi.h | 1 - > 6 files changed, 16 insertions(+), 25 deletions(-) > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c >b/drivers/media/platform/chips-media/wave5/wave5-helper.c >index 8433ecab230c..ec710b838dfe 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c >@@ -29,9 +29,6 @@ void wave5_cleanup_instance(struct vpu_instance *inst) > { > int i; > >- if (list_is_singular(&inst->list)) >- wave5_vdi_free_sram(inst->dev); >- > for (i = 0; i < inst->fbc_buf_count; i++) > wave5_vpu_dec_reset_framebuffer(inst, i); > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c >b/drivers/media/platform/chips-media/wave5/wave5-vdi.c >index 3809f70bc0b4..ee671f5a2f37 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c >@@ -174,16 +174,19 @@ int wave5_vdi_allocate_array(struct vpu_device >*vpu_dev, struct vpu_buf *array, > void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) > { > struct vpu_buf *vb = &vpu_dev->sram_buf; >+ dma_addr_t daddr; >+ void *vaddr; >+ size_t size; > >- if (!vpu_dev->sram_pool || !vpu_dev->sram_size) >+ if (!vpu_dev->sram_pool || vb->vaddr) > return; > >- if (!vb->vaddr) { >- vb->size = vpu_dev->sram_size; >- vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, >- &vb->daddr); >- if (!vb->vaddr) >- vb->size = 0; >+ size = gen_pool_size(vpu_dev->sram_pool); >+ vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr); >+ if (vaddr) { >+ vb->vaddr = vaddr; >+ vb->daddr = daddr; >+ vb->size = size; > } > > dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: >0x%p\n", >@@ -197,9 +200,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev) > if (!vb->size || !vb->vaddr) > return; > >- if (vb->vaddr) >- gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, >- vb->size); >+ gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb- >>size); > > memset(vb, 0, sizeof(*vb)); > } >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >index aa0401f35d32..84dbe56216ad 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >@@ -1854,8 +1854,6 @@ static int wave5_vpu_open_dec(struct file *filp) > goto cleanup_inst; > } > >- wave5_vdi_allocate_sram(inst->dev); >- > return 0; > > cleanup_inst: >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c >b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c >index 8bbf9d10b467..86ddcb82443b 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c >@@ -1727,8 +1727,6 @@ static int wave5_vpu_open_enc(struct file *filp) > goto cleanup_inst; > } > >- wave5_vdi_allocate_sram(inst->dev); >- > return 0; > > cleanup_inst: >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c >b/drivers/media/platform/chips-media/wave5/wave5-vpu.c >index f3ecadefd37a..2a0a70dd7062 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c >@@ -178,16 +178,11 @@ static int wave5_vpu_probe(struct platform_device >*pdev) > return ret; > } > >- ret = of_property_read_u32(pdev->dev.of_node, "sram-size", >- &dev->sram_size); >- if (ret) { >- dev_warn(&pdev->dev, "sram-size not found\n"); >- dev->sram_size = 0; >- } >- Required SRAM size is different from each wave5 product. And, SoC vendor also can configure the different SRAM size depend on target SoC specification even they use the same wave5 product. Thanks. Nas. > dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); > if (!dev->sram_pool) > dev_warn(&pdev->dev, "sram node not found\n"); >+ else >+ wave5_vdi_allocate_sram(dev); > > dev->product_code = wave5_vdi_read_register(dev, >VPU_PRODUCT_CODE_REGISTER); > ret = wave5_vdi_init(&pdev->dev); >@@ -259,6 +254,8 @@ static int wave5_vpu_probe(struct platform_device >*pdev) > err_clk_dis: > clk_bulk_disable_unprepare(dev->num_clks, dev->clks); > >+ wave5_vdi_free_sram(dev); >+ > return ret; > } > >@@ -275,6 +272,7 @@ static void wave5_vpu_remove(struct platform_device >*pdev) > v4l2_device_unregister(&dev->v4l2_dev); > wave5_vdi_release(&pdev->dev); > ida_destroy(&dev->inst_ida); >+ wave5_vdi_free_sram(dev); > } > > static const struct wave5_match_data ti_wave521c_data = { >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h >b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h >index fa62a85080b5..8d88381ac55e 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h >@@ -749,7 +749,6 @@ struct vpu_device { > struct vpu_attr attr; > struct vpu_buf common_mem; > u32 last_performance_cycles; >- u32 sram_size; > struct gen_pool *sram_pool; > struct vpu_buf sram_buf; > void __iomem *vdb_register; >-- >2.44.0
[-- Attachment #1: Type: text/plain, Size: 8096 bytes --] On Mon, Mar 18, 2024 at 06:11:30PM +0200, Ville Syrjälä wrote: > On Mon, Mar 18, 2024 at 02:49:47PM +0100, Maxime Ripard wrote: > > Hi, > > > > On Fri, Mar 15, 2024 at 10:22:05AM +0200, Ville Syrjälä wrote: > > > On Mon, Mar 11, 2024 at 03:49:48PM +0100, Maxime Ripard wrote: > > > > Infoframes in KMS is usually handled by a bunch of low-level helpers > > > > that require quite some boilerplate for drivers. This leads to > > > > discrepancies with how drivers generate them, and which are actually > > > > sent. > > > > > > > > Now that we have everything needed to generate them in the HDMI > > > > connector state, we can generate them in our common logic so that > > > > drivers can simply reuse what we precomputed. > > > > > > > > Signed-off-by: Maxime Ripard <mripard@kernel.org> > > > > --- > > > > drivers/gpu/drm/Kconfig | 1 + > > > > drivers/gpu/drm/drm_atomic_state_helper.c | 323 +++++++++++++++++++++ > > > > drivers/gpu/drm/drm_connector.c | 14 + > > > > .../gpu/drm/tests/drm_atomic_state_helper_test.c | 1 + > > > > drivers/gpu/drm/tests/drm_connector_test.c | 12 + > > > > include/drm/drm_atomic_state_helper.h | 8 + > > > > include/drm/drm_connector.h | 133 +++++++++ > > > > 7 files changed, 492 insertions(+) > > > > > > > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > > > > index 872edb47bb53..ad9c467e20ce 100644 > > > > --- a/drivers/gpu/drm/Kconfig > > > > +++ b/drivers/gpu/drm/Kconfig > > > > @@ -97,10 +97,11 @@ config DRM_KUNIT_TEST > > > > If in doubt, say "N". > > > > > > > > config DRM_KMS_HELPER > > > > tristate > > > > depends on DRM > > > > + select DRM_DISPLAY_HDMI_HELPER > > > > help > > > > CRTC helpers for KMS drivers. > > > > > > > > config DRM_DEBUG_DP_MST_TOPOLOGY_REFS > > > > bool "Enable refcount backtrace history in the DP MST helpers" > > > > diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c > > > > index e66272c0d006..2bf53666fc9d 100644 > > > > --- a/drivers/gpu/drm/drm_atomic_state_helper.c > > > > +++ b/drivers/gpu/drm/drm_atomic_state_helper.c > > > > @@ -36,10 +36,12 @@ > > > > #include <drm/drm_plane.h> > > > > #include <drm/drm_print.h> > > > > #include <drm/drm_vblank.h> > > > > #include <drm/drm_writeback.h> > > > > > > > > +#include <drm/display/drm_hdmi_helper.h> > > > > + > > > > #include <linux/slab.h> > > > > #include <linux/dma-fence.h> > > > > > > > > /** > > > > * DOC: atomic state reset and initialization > > > > @@ -912,10 +914,143 @@ hdmi_compute_config(const struct drm_connector *connector, > > > > } > > > > > > > > return -EINVAL; > > > > } > > > > > > > > +static int hdmi_generate_avi_infoframe(const struct drm_connector *connector, > > > > + struct drm_connector_state *state) > > > > +{ > > > > + const struct drm_display_mode *mode = > > > > + connector_state_get_mode(state); > > > > + struct drm_connector_hdmi_infoframe *infoframe = > > > > + &state->hdmi.infoframes.avi; > > > > + struct hdmi_avi_infoframe *frame = > > > > + &infoframe->data.avi; > > > > + bool is_full_range = state->hdmi.is_full_range; > > > > + enum hdmi_quantization_range rgb_quant_range = > > > > + is_full_range ? HDMI_QUANTIZATION_RANGE_FULL : HDMI_QUANTIZATION_RANGE_LIMITED; > > > > + int ret; > > > > + > > > > + ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + frame->colorspace = state->hdmi.output_format; > > > > + > > > > + drm_hdmi_avi_infoframe_quant_range(frame, connector, mode, rgb_quant_range); > > > > > > drm_hdmi_avi_infoframe_quant_range() doesn't handle YCbCr currently. > > > > I guess it's not really a problem anymore if we drop YUV422 selection, > > but I'll add a comment. > > > > > > + drm_hdmi_avi_infoframe_colorimetry(frame, state); > > > > + drm_hdmi_avi_infoframe_bars(frame, state); > > > > + > > > > + infoframe->set = true; > > > > + > > > > + return 0; > > > > +} > > > > + > > > <snip> > > > > + > > > > +#define UPDATE_INFOFRAME(c, os, ns, i) \ > > > > + write_or_clear_infoframe(c, \ > > > > + &(c)->hdmi.infoframes.i, \ > > > > + &(os)->hdmi.infoframes.i, \ > > > > + &(ns)->hdmi.infoframes.i) > > > > > > This macro feels like pointless obfuscation to me. > > > > I'll remove it then. > > > > > <snip> > > > > @@ -1984,20 +2063,73 @@ struct drm_connector { > > > > > > > > /** > > > > * @hdmi: HDMI-related variable and properties. > > > > */ > > > > struct { > > > > +#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8 > > > > + /** > > > > + * @vendor: HDMI Controller Vendor Name > > > > + */ > > > > + unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring; > > > > + > > > > +#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16 > > > > + /** > > > > + * @product: HDMI Controller Product Name > > > > + */ > > > > + unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring; > > > > + > > > > /** > > > > * @supported_formats: Bitmask of @hdmi_colorspace > > > > * supported by the controller. > > > > */ > > > > unsigned long supported_formats; > > > > > > > > /** > > > > * @funcs: HDMI connector Control Functions > > > > */ > > > > const struct drm_connector_hdmi_funcs *funcs; > > > > + > > > > + /** > > > > + * @infoframes: Current Infoframes output by the connector > > > > + */ > > > > + struct { > > > > + /** > > > > + * @lock: Mutex protecting against concurrent access to > > > > + * the infoframes, most notably between KMS and ALSA. > > > > + */ > > > > + struct mutex lock; > > > > + > > > > + /** > > > > + * @audio: Current Audio Infoframes structure. Protected > > > > + * by @lock. > > > > + */ > > > > + struct drm_connector_hdmi_infoframe audio; > > > > + > > > > + /** > > > > + * @avi: Current AVI Infoframes structure. Protected by > > > > + * @lock. > > > > + */ > > > > + struct drm_connector_hdmi_infoframe avi; > > > > + > > > > + /** > > > > + * @hdr_drm: Current DRM (Dynamic Range and Mastering) > > > > + * Infoframes structure. Protected by @lock. > > > > + */ > > > > + struct drm_connector_hdmi_infoframe hdr_drm; > > > > + > > > > + /** > > > > + * @spd: Current SPD Infoframes structure. Protected by > > > > + * @lock. > > > > + */ > > > > + struct drm_connector_hdmi_infoframe spd; > > > > + > > > > + /** > > > > + * @vendor: Current HDMI Vendor Infoframes structure. > > > > + * Protected by @lock. > > > > + */ > > > > + struct drm_connector_hdmi_infoframe hdmi; > > > > + } infoframes; > > > > } hdmi; > > > > > > What's the deal with this bloat? These are already tracked in the > > > connector's state so this looks entirely redundant. > > > > The next patch in this series is about adding debugfs entries to read > > the infoframes, and thus we need to care about concurrency between > > debugfs files accesses and commits. Copying the things we care about > > from the state to the entity is the typical solution for that, but I > > guess we could also take the proper locks and access the current > > connector state. > > Yeah, just lock and dump the latest state. That is the only thing > that should of interest to anyone in userspace. > > Also are you actually adding some kind of ad-hoc state dump things > just for these? Why not do whatever is needed to include them in > the normal .atomic_state_print() stuff? Yeah, part of the reason for the whole thing is so we can make edid-decode check the sanity of generated infoframes, for both v4l2 and DRM. Hans has been working on it and has a prototype based on this work. But you're right, we should probably add them to atomic_state_print too Maxime [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --]
Hi, Ivan. >-----Original Message----- >From: Ivan Bornyakov <brnkv.i1@gmail.com> >Sent: Monday, March 18, 2024 11:42 PM >To: Nas Chung <nas.chung@chipsnmedia.com>; jackson.lee ><jackson.lee@chipsnmedia.com>; Mauro Carvalho Chehab <mchehab@kernel.org> >Cc: Ivan Bornyakov <brnkv.i1@gmail.com>; Philipp Zabel ><p.zabel@pengutronix.de>; Rob Herring <robh@kernel.org>; Krzysztof >Kozlowski <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley ><conor+dt@kernel.org>; linux-media@vger.kernel.org; linux- >kernel@vger.kernel.org; devicetree@vger.kernel.org >Subject: [PATCH 1/6] media: chips-media: wave5: support decoding higher >bit-depth profiles > >Add support for decoding higher than 8 bit-depth profiles by scaling FBC >buffer stride and size by the factor of (bitdepth / 8). > >Signed-off-by: Ivan Bornyakov <brnkv.i1@gmail.com> >--- > .../platform/chips-media/wave5/wave5-vpu-dec.c | 15 ++------------- > 1 file changed, 2 insertions(+), 13 deletions(-) > >diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >index ef227af72348..aa0401f35d32 100644 >--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c >@@ -1055,6 +1055,7 @@ static int wave5_prepare_fb(struct vpu_instance >*inst) > int ret, i; > struct v4l2_m2m_buffer *buf, *n; > struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; >+ u32 bitdepth = inst->codec_info- >>dec_info.initial_info.luma_bitdepth; > > linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); > non_linear_num = inst->fbc_buf_count; >@@ -1063,7 +1064,7 @@ static int wave5_prepare_fb(struct vpu_instance >*inst) > struct frame_buffer *frame = &inst->frame_buf[i]; > struct vpu_buf *vframe = &inst->frame_vbuf[i]; > >- fb_stride = inst->dst_fmt.width; >+ fb_stride = ALIGN(inst->dst_fmt.width * bitdepth / 8, 32); > fb_height = ALIGN(inst->dst_fmt.height, 32); > luma_size = fb_stride * fb_height; > >@@ -1408,22 +1409,10 @@ static int wave5_vpu_dec_start_streaming(struct >vb2_queue *q, unsigned int count > if (ret) > goto free_bitstream_vbuf; > } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { >- struct dec_initial_info *initial_info = >- &inst->codec_info->dec_info.initial_info; >- > if (inst->state == VPU_INST_STATE_STOP) > ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); > if (ret) > goto return_buffers; >- >- if (inst->state == VPU_INST_STATE_INIT_SEQ) { >- if (initial_info->luma_bitdepth != 8) { >- dev_info(inst->dev->dev, "%s: no support for %d >bit depth", >- __func__, initial_info->luma_bitdepth); >- ret = -EINVAL; >- goto return_buffers; >- } >- } > } > TI wave521C version cannot support 10bit decoding. So, We need above error checking codes. How about adding support_10bit flag in wave5_match_data like below ? static const struct wave5_match_data ti_wave521c_data = { .support_10bit = false; }; > return ret; >-- >2.44.0
Hi Linh Could you share the "lsusb -v " info of your device, and also what are the raw values from the device for UVC_GET_MAX, UVC_GET_MIN for UVC_CT_PANTILT_RELATIVE_CONTROL and UVC_CT_ZOOM_RELATIVE_CONTROL ? Something like this (not tested :) ): diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e59a463c27618..76c7adc3aa579 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -415,7 +415,11 @@ static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); case UVC_GET_MIN: + printk(KERN_ERR "zoom: GET_MIN 0%x 0x%x 0%x\n", data[0], data[1], data[2]); + return data[2]; case UVC_GET_MAX: + printk(KERN_ERR "zoom: GET_MAX 0%x 0x%x 0%x\n", data[0], data[1], data[2]); + fallthrough; case UVC_GET_RES: case UVC_GET_DEF: default: @@ -441,8 +445,11 @@ static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, return (rel == 0) ? 0 : (rel > 0 ? data[first+1] : -data[first+1]); case UVC_GET_MIN: + printk(KERN_ERR "speed: GET_MIN 0%x 0x%x 0%x\n", data[first], data[first+1], data[first+2]); return -data[first+1]; case UVC_GET_MAX: + printk(KERN_ERR "speed: GET_MAX 0%x 0x%x 0%x\n", data[first], data[first+1], data[first+2]); + fallthrough; case UVC_GET_RES: case UVC_GET_DEF: default: Thanks! On Tue, 19 Mar 2024 at 06:30, Linh Vu <linh.tp.vu@gmail.com> wrote: > > When mapping from UVC_CT_PANTILT_RELATIVE_CONTROL > to V4L2_CID_PAN_SPEED and V4L2_CID_TILT_SPEED, > and from UVC_CT_ZOOM_RELATIVE_CONTROL to V4L2_CID_ZOOM_CONTINUOUS, > the minimum value of the movement should be negated of the maximum value. > > For example, if a UVC device (e.g., OBSBOT Tiny 2) declares a pan speed > range [1, 160], its V4L2_CID_PAN_SPEED mapping has range [-160, 160]. > > Currently, calling ioctl with VIDIOC_QUERY_EXT_CTRL and V4L2_CID_PAN_SPEED > returns a minimum value of -1. When calling ioctl with VIDIOC_S_CTRL, > V4L2_CID_PAN_SPEED and -100, the speed (velocity) of the pan movement gets > clamped to -1. > > To get the minimum value of V4L2_CID_PAN_SPEED, > uvc_ctrl_get_rel_speed is called with > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN) as data, > which should be uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX). > > The same thing should be done for V4L2_CID_TILT_SPEED and > V4L2_CID_ZOOM_CONTINUOUS. > > For V4L2_CID_ZOOM_CONTINUOUS, uvc_ctrl_get_zoom does not add the > sign to the returned minimum value, so it's impossible to zoom out. > > Modify the data that is passed when querying the minimum > value for V4L2_CID_PAN_SPEED, V4L2_CID_TILT_SPEED and > V4L2_CID_ZOOM_CONTINUOUS. > Also add sign to the returned minimum value in uvc_ctrl_get_zoom. > Thus, the correct minimum value for relative PTZ controls can be returned. > > Signed-off-by: Linh Vu <linh.tp.vu@gmail.com> > --- > drivers/media/usb/uvc/uvc_ctrl.c | 24 +++++++++++++++++++----- > 1 file changed, 19 insertions(+), 5 deletions(-) > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c > index e59a463c27618..00fd7e74e6d6b 100644 > --- a/drivers/media/usb/uvc/uvc_ctrl.c > +++ b/drivers/media/usb/uvc/uvc_ctrl.c > @@ -415,6 +415,7 @@ static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, > return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); > > case UVC_GET_MIN: > + return -data[2]; > case UVC_GET_MAX: > case UVC_GET_RES: > case UVC_GET_DEF: > @@ -1322,9 +1323,16 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, > break; > } > > - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) > - v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, > - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); > + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { > + if (v4l2_ctrl->id == V4L2_CID_PAN_SPEED > + || v4l2_ctrl->id == V4L2_CID_TILT_SPEED > + || v4l2_ctrl->id == V4L2_CID_ZOOM_CONTINUOUS) > + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); > + else > + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); > + } > > if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) > v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, > @@ -1909,9 +1917,15 @@ int uvc_ctrl_set(struct uvc_fh *handle, > if (ret < 0) > return ret; > } > + if (mapping->id == V4L2_CID_PAN_SPEED > + || mapping->id == V4L2_CID_TILT_SPEED > + || mapping->id == V4L2_CID_ZOOM_CONTINUOUS) > + min = mapping->get(mapping, UVC_GET_MIN, > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); > + else > + min = mapping->get(mapping, UVC_GET_MIN, > + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); > > - min = mapping->get(mapping, UVC_GET_MIN, > - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); > max = mapping->get(mapping, UVC_GET_MAX, > uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); > step = mapping->get(mapping, UVC_GET_RES, > > base-commit: e0b8eb0f6d652981bfd9ba7c619c9d81ed087ad0 > -- > 2.34.1 > > -- Ricardo Ribalda
Hi Gergo I missed sending the reviewed-by sorry :) Btw, do you still have access to the device? Could you tell me what you get from and UVC_GET_MAX, UVC_GET_MIN for UVC_CT_PANTILT_RELATIVE_CONTROL and UVC_CT_ZOOM_RELATIVE_CONTROL ? Thanks! On Mon, 20 Nov 2023 at 19:53, Gergo Koteles <soyer@irl.hu> wrote: > > Devices with pan/tilt controls but without pan/tilt speed controls > return 1 for the default value of V4L2_CID_PAN_SPEED or > V4L2_CID_TILT_SPEED. For these controls, the value of 1 means a > move and that's not a good default. > > Currently, for these controls the UVC_GET_DEF query returns > bPanSpeed or bTiltSpeed of CT_PANTILT_RELATIVE_CONTROL. > > According to the UVC 1.5 specification, the default value of bPanSpeed > or bTiltSpeed should be 1 if the pan/tilt control doesn't support > speed control. > > "If the control does not support speed control for the Tilt control, > it will return the value 1 in this field for all these requests." > > This patch modifies the uvc_ctrl_get_rel_speed to return hardcoded 0 > for UVC_GET_DEF query, because that's the stop or don't move value > for these V4L2 controls. > > Previous discussion > Link: https://lore.kernel.org/all/CAP_ceTy6XVmvTTAmvCp1YU2wxHwXqnarm69Yaz8K4FmpJqYxAg@mail.gmail.com/ > > Signed-off-by: Gergo Koteles <soyer@irl.hu> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org> > --- > drivers/media/usb/uvc/uvc_ctrl.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c > index 5e9d3da862dd..e131958c0930 100644 > --- a/drivers/media/usb/uvc/uvc_ctrl.c > +++ b/drivers/media/usb/uvc/uvc_ctrl.c > @@ -444,9 +444,10 @@ static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, > return -data[first+1]; > case UVC_GET_MAX: > case UVC_GET_RES: > + return data[first+1]; > case UVC_GET_DEF: > default: > - return data[first+1]; > + return 0; > } > } > > > base-commit: be9aac187433af6abba5fcc2e73d91d0794ba360 > -- > 2.42.0 > > -- Ricardo Ribalda
On 19/03/2024 08:00, Tomi Valkeinen wrote:
> On 19/03/2024 08:48, Tomi Valkeinen wrote:
>> On 19/03/2024 08:23, Krzysztof Kozlowski wrote:
>>> On 18/03/2024 16:49, Tomi Valkeinen wrote:
>>>> Add DT bindings for raspberrypi,rp1-cfe.
>>>>
>>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>>>> ---
>>>> .../bindings/media/raspberrypi,rp1-cfe.yaml | 103
>>>> +++++++++++++++++++++
>>>> 1 file changed, 103 insertions(+)
>>>>
>>>> diff --git
>>>> a/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>> b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>> new file mode 100644
>>>> index 000000000000..7b2beeaaab0e
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
>>>> @@ -0,0 +1,103 @@
>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>> +%YAML 1.2
>>>> +---
>>>> +$id: http://devicetree.org/schemas/media/raspberrypi,rp1-cfe.yaml#
>>>
>>> Use compatible as filename.
>>
>> Ah, indeed. I changed the compatible quite late, adding the "rpi5" as
>> versioning, and missed changing the file name.
>>
>> I'll rename.
>
> Actually, maybe it's better to have two compatibles,
> "raspberrypi,rp1-cfe" as the generic one, and "raspberrypi,rpi5-rp1-cfe"
> (or something similar) for RaspberryPi 5.
>
> And I'm not sure if the "rp1" part is relevant there, would
> "raspberrypi,cfe" be just as fine? Naush?
See writing bindings. Compatibles should be SoC specific. In some cases
generic fallbacks make sense, in some note. But don't just choose
"generic fallback" because you want. Provide rationale.
Best regards,
Krzysztof
On 19/03/2024 07:46, Tomi Valkeinen wrote: >>> + reg: >>> + items: >>> + - description: CSI-2 registers >>> + - description: D-PHY registers >>> + - description: MIPI CFG (a simple top-level mux) registers >>> + - description: FE registers >>> + >>> + interrupts: >>> + maxItems: 1 >>> + >>> + clocks: >>> + maxItems: 1 >>> + >>> + port: >>> + $ref: /schemas/graph.yaml#/$defs/port-base >>> + additionalProperties: false >>> + description: CSI-2 RX Port >> >> Only one port, so there is nothing to output to? > > The CFE has DMA, so it writes to memory. But no other outputs. You still might have some sort of pipeline, e.g. some post processing block. But if this is end block, then I guess it's fine. > >>> + >>> + properties: >>> + endpoint: >>> + $ref: video-interfaces.yaml# >>> + unevaluatedProperties: false >>> + >>> + properties: >>> + data-lanes: >>> + minItems: 1 >>> + maxItems: 4 >>> + >>> + clock-lanes: >>> + maxItems: 1 >>> + >>> + clock-noncontinuous: true >> >> Drop > > Hmm, I saw this used in multiple other bindings, and thought it means > the property is allowed and copied it here. > > If that's not the case, does this mean all the properties from > video-interfaces.yaml are allowed (even invalid ones, like pclk-sample)? Yes, that's the meaning of unevaluatedProperties you have a bit above. > >>> + >>> + required: >>> + - clock-lanes >>> + - data-lanes >>> + >>> +required: >>> + - compatible >>> + - reg >>> + - interrupts >>> + - clocks >>> + >>> +additionalProperties: false >>> + >>> +examples: >>> + - | >>> + #include <dt-bindings/clock/rp1.h> >>> + #include <dt-bindings/interrupt-controller/irq.h> >>> + #include <dt-bindings/mfd/rp1.h> >>> + >>> + rpi1 { >> >> soc > > That should actually be "rp1", not "rpi1". rp1 is the co-processor on > which the cfe is located, so it doesn't reside in the soc itself. But Where is the co-processor located? > perhaps that's not relevant, and "soc" is just a generic container that > should always be used? Does not have to be soc, but rp1 is not generic. Node names should be generic. See also an explanation and list of examples (not exhaustive) in DT specification: https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation Best regards, Krzysztof
Hi, Julien: On Wed, 2024-01-10 at 15:14 +0100, Julien Stephan wrote: > > External email : Please do not click links or open attachments until > you have verified the sender or the content. > From: Phi-bang Nguyen <pnguyen@baylibre.com> > > This driver provides a path to bypass the SoC ISP so that image data > coming from the SENINF can go directly into memory without any image > processing. This allows the use of an external ISP. > > Signed-off-by: Phi-bang Nguyen <pnguyen@baylibre.com> > Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com> > [Paul Elder fix irq locking] > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Co-developed-by: Julien Stephan <jstephan@baylibre.com> > Signed-off-by: Julien Stephan <jstephan@baylibre.com> > --- [snip] > + > +#define IMG_MAX_WIDTH 5376 > +#define IMG_MAX_HEIGHT 4032 > +#define IMG_DEF_WIDTH 1920 > +#define IMG_DEF_HEIGHT 1080 Why do you define default width/height? Does 1920x1080 have any benefit? If so, add comment to describe why choose 1920x1080. If no, I think using IMG_MAX_WIDTH/IMG_MAX_HEIGHT as default is a good choice because we could drop this redundant definition and let hardware work in its best quality. Regards, CK > +#define IMG_MIN_WIDTH 80 > +#define IMG_MIN_HEIGHT 60 > +
On (24/03/18 23:55), Ricardo Ribalda wrote:
>
> The UVC standard descries the values for the PLF control for its
> versions 1.1 and and 1.5, but it does not describe which values MUST be
> implemented.
>
> So far, we have been adding "device quirks" to provide proper limits for
> the control, but this is failing to scale.
>
> Add a function that during probe-time checks the capability of the
> control.
>
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
Looks good to me
FWIW
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
From: Hans Verkuil <hverkuil@xs4all.nl> This adds support for the fraction_bits field, used with integer controls. This allows fixed point formats to be described. The fraction_bits field is only exposed through VIDIOC_QUERY_EXT_CTRL. For a given signed two's complement Qf fixed point value 'f' equals fraction_bits. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> --- .../media/v4l/vidioc-queryctrl.rst | 11 ++- drivers/media/v4l2-core/v4l2-ctrls-api.c | 1 + drivers/media/v4l2-core/v4l2-ctrls-core.c | 93 +++++++++++++++---- include/media/v4l2-ctrls.h | 7 +- include/uapi/linux/videodev2.h | 3 +- 5 files changed, 95 insertions(+), 20 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst index 4d38acafe8e1..e65c7e5d78ec 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst @@ -267,7 +267,16 @@ See also the examples in :ref:`control`. - The size of each dimension. The first ``nr_of_dims`` elements of this array must be non-zero, all remaining elements must be zero. * - __u32 - - ``reserved``\ [32] + - ``fraction_bits`` + - The number of least significant bits of the control value that + form the fraction of the fixed point value. This is 0 if the value + is a regular integer. This can be used with all integer control types + (``INTEGER``, ``INTEGER64``, ``U8``, ``U16`` and ``U32``). + For the signed types the signed two's complement representation is used. + This field applies to the control value as well as the ``minimum``, + ``maximum``, ``step`` and ``default_value`` fields. + * - __u32 + - ``reserved``\ [31] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index d9a422017bd9..ef16b00421ec 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -1101,6 +1101,7 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr qc->elems = ctrl->elems; qc->nr_of_dims = ctrl->nr_of_dims; memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0])); + qc->fraction_bits = ctrl->fraction_bits; qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index c4d995f32191..d83a37198bb5 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -252,12 +252,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, } EXPORT_SYMBOL(v4l2_ctrl_type_op_init); +static void v4l2_ctrl_log_fp(s64 v, unsigned int fraction_bits) +{ + s64 i, f, mask; + + if (!fraction_bits) { + pr_cont("%lld", v); + return; + } + + mask = (1ULL << fraction_bits) - 1; + + /* + * Note: this function does not support fixed point u64 with + * fraction_bits set to 64. At the moment there is no U64 + * control type, but if that is added, then this code will have + * to add support for it. + */ + if (fraction_bits >= 63) + i = v < 0 ? -1 : 0; + else + i = div64_s64(v, 1LL << fraction_bits); + + f = v < 0 ? -((-v) & mask) : (v & mask); + + if (!f) { + pr_cont("%lld", i); + } else if (fraction_bits < 20) { + u64 div = 1ULL << fraction_bits; + + if (!i && f < 0) + pr_cont("-%lld/%llu", -f, div); + else if (!i) + pr_cont("%lld/%llu", f, div); + else if (i < 0 || f < 0) + pr_cont("-%lld-%llu/%llu", -i, -f, div); + else + pr_cont("%lld+%llu/%llu", i, f, div); + } else { + if (!i && f < 0) + pr_cont("-%lld/(2^%u)", -f, fraction_bits); + else if (!i) + pr_cont("%lld/(2^%u)", f, fraction_bits); + else if (i < 0 || f < 0) + pr_cont("-%lld-%llu/(2^%u)", -i, -f, fraction_bits); + else + pr_cont("%lld+%llu/(2^%u)", i, f, fraction_bits); + } +} + void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) { union v4l2_ctrl_ptr ptr = ctrl->p_cur; if (ctrl->is_array) { - unsigned i; + unsigned int i; for (i = 0; i < ctrl->nr_of_dims; i++) pr_cont("[%u]", ctrl->dims[i]); @@ -266,7 +315,7 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - pr_cont("%d", *ptr.p_s32); + v4l2_ctrl_log_fp(*ptr.p_s32, ctrl->fraction_bits); break; case V4L2_CTRL_TYPE_BOOLEAN: pr_cont("%s", *ptr.p_s32 ? "true" : "false"); @@ -281,19 +330,19 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) pr_cont("0x%08x", *ptr.p_s32); break; case V4L2_CTRL_TYPE_INTEGER64: - pr_cont("%lld", *ptr.p_s64); + v4l2_ctrl_log_fp(*ptr.p_s64, ctrl->fraction_bits); break; case V4L2_CTRL_TYPE_STRING: pr_cont("%s", ptr.p_char); break; case V4L2_CTRL_TYPE_U8: - pr_cont("%u", (unsigned)*ptr.p_u8); + v4l2_ctrl_log_fp(*ptr.p_u8, ctrl->fraction_bits); break; case V4L2_CTRL_TYPE_U16: - pr_cont("%u", (unsigned)*ptr.p_u16); + v4l2_ctrl_log_fp(*ptr.p_u16, ctrl->fraction_bits); break; case V4L2_CTRL_TYPE_U32: - pr_cont("%u", (unsigned)*ptr.p_u32); + v4l2_ctrl_log_fp(*ptr.p_u32, ctrl->fraction_bits); break; case V4L2_CTRL_TYPE_H264_SPS: pr_cont("H264_SPS"); @@ -1753,11 +1802,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size, - u32 flags, const char * const *qmenu, + u32 fraction_bits, u32 flags, const char * const *qmenu, const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def, void *priv) { struct v4l2_ctrl *ctrl; + unsigned int max_fraction_bits = 0; unsigned sz_extra; unsigned nr_of_dims = 0; unsigned elems = 1; @@ -1779,20 +1829,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, /* Prefill elem_size for all types handled by std_type_ops */ switch ((u32)type) { + case V4L2_CTRL_TYPE_INTEGER: + elem_size = sizeof(s32); + max_fraction_bits = 31; + break; case V4L2_CTRL_TYPE_INTEGER64: elem_size = sizeof(s64); + max_fraction_bits = 63; break; case V4L2_CTRL_TYPE_STRING: elem_size = max + 1; break; case V4L2_CTRL_TYPE_U8: elem_size = sizeof(u8); + max_fraction_bits = 8; break; case V4L2_CTRL_TYPE_U16: elem_size = sizeof(u16); + max_fraction_bits = 16; break; case V4L2_CTRL_TYPE_U32: elem_size = sizeof(u32); + max_fraction_bits = 32; break; case V4L2_CTRL_TYPE_MPEG2_SEQUENCE: elem_size = sizeof(struct v4l2_ctrl_mpeg2_sequence); @@ -1876,10 +1934,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } /* Sanity checks */ - if (id == 0 || name == NULL || !elem_size || - id >= V4L2_CID_PRIVATE_BASE || - (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || - (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { + if (id == 0 || !name || !elem_size || + fraction_bits > max_fraction_bits || id >= V4L2_CID_PRIVATE_BASE || + (type == V4L2_CTRL_TYPE_MENU && !qmenu) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && !qmenu_int)) { handler_set_err(hdl, -ERANGE); return NULL; } @@ -1940,6 +1998,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->name = name; ctrl->type = type; ctrl->flags = flags; + ctrl->fraction_bits = fraction_bits; ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; @@ -2038,7 +2097,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, - cfg->dims, cfg->elem_size, + cfg->dims, cfg->elem_size, cfg->fraction_bits, flags, qmenu, qmenu_int, cfg->p_def, priv); if (ctrl) ctrl->is_private = cfg->is_private; @@ -2063,7 +2122,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - min, max, step, def, NULL, 0, + min, max, step, def, NULL, 0, 0, flags, NULL, NULL, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -2096,7 +2155,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, NULL, 0, + 0, max, mask, def, NULL, 0, 0, flags, qmenu, qmenu_int, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -2128,7 +2187,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, NULL, 0, + 0, max, mask, def, NULL, 0, 0, flags, qmenu, NULL, ptr_null, NULL); } @@ -2150,7 +2209,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - min, max, step, def, NULL, 0, + min, max, step, def, NULL, 0, 0, flags, NULL, NULL, p_def, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_compound); @@ -2174,7 +2233,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, 0, def, NULL, 0, + 0, max, 0, def, NULL, 0, 0, flags, NULL, qmenu_int, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 59679a42b3e7..c35514c5bf88 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -211,7 +211,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * except for dynamic arrays. In that case it is in the range of * 1 to @p_array_alloc_elems. * @dims: The size of each dimension. - * @nr_of_dims:The number of dimensions in @dims. + * @nr_of_dims: The number of dimensions in @dims. + * @fraction_bits: The number of fraction bits for fixed point values. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -228,6 +229,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * :math:`ceil(\frac{maximum - minimum}{step}) + 1`. * Used only if the @type is %V4L2_CTRL_TYPE_INTEGER_MENU. * @flags: The control's flags. + * @fraction_bits: The number of fraction bits for fixed point values. * @priv: The control's private pointer. For use by the driver. It is * untouched by the control framework. Note that this pointer is * not freed when the control is deleted. Should this be needed @@ -286,6 +288,7 @@ struct v4l2_ctrl { u32 new_elems; u32 dims[V4L2_CTRL_MAX_DIMS]; u32 nr_of_dims; + u32 fraction_bits; union { u64 step; u64 menu_skip_mask; @@ -426,6 +429,7 @@ struct v4l2_ctrl_handler { * @dims: The size of each dimension. * @elem_size: The size in bytes of the control. * @flags: The control's flags. + * @fraction_bits: The number of fraction bits for fixed point values. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -455,6 +459,7 @@ struct v4l2_ctrl_config { u32 dims[V4L2_CTRL_MAX_DIMS]; u32 elem_size; u32 flags; + u32 fraction_bits; u64 menu_skip_mask; const char * const *qmenu; const s64 *qmenu_int; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index a8015e5e7fa4..b8573e9ccde6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1947,7 +1947,8 @@ struct v4l2_query_ext_ctrl { __u32 elems; __u32 nr_of_dims; __u32 dims[V4L2_CTRL_MAX_DIMS]; - __u32 reserved[32]; + __u32 fraction_bits; + __u32 reserved[31]; }; /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ -- 2.34.1
Audio signal processing also has the requirement for memory to memory similar as Video. This asrc memory to memory (memory ->asrc->memory) case is a non real time use case. User fills the input buffer to the asrc module, after conversion, then asrc sends back the output buffer to user. So it is not a traditional ALSA playback and capture case. It is a specific use case, there is no reference in current kernel. v4l2 memory to memory is the closed implementation, v4l2 current support video, image, radio, tuner, touch devices, so it is not complicated to add support for this specific audio case. Because we had implemented the "memory -> asrc ->i2s device-> codec" use case in ALSA. Now the "memory->asrc->memory" needs to reuse the code in asrc driver, so the first 3 patches is for refining the code to make it can be shared by the "memory->asrc->memory" driver. The main change is in the v4l2 side, A /dev/vl4-audioX will be created, user applications only use the ioctl of v4l2 framework. Other change is to add memory to memory support for two kinds of i.MX ASRC module. changes in v15: - update MAINTAINERS for imx-asrc.c and vim2m-audio.c changes in v14: - document the reservation of 'AUXX' fourcc format. - add v4l2_audfmt_to_fourcc() definition. changes in v13 - change 'pixelformat' to 'audioformat' in dev-audio-mem2mem.rst - add more description for clock drift in ext-ctrls-audio-m2m.rst - Add "media: v4l2-ctrls: add support for fraction_bits" from Hans to avoid build issue for kernel test robot changes in v12 - minor changes according to comments - drop min_buffers_needed = 1 and V4L2_CTRL_FLAG_UPDATE flag - drop bus_info changes in v11 - add add-fixed-point-test-controls in vivid. - add v4l2_ctrl_fp_compose() helper function for min and max changes in v10 - remove FIXED_POINT type - change code base on media: v4l2-ctrls: add support for fraction_bits - fix issue reported by kernel test robot - remove module_alias changes in v9: - add MEDIA_ENT_F_PROC_AUDIO_RESAMPLER. - add MEDIA_INTF_T_V4L_AUDIO - add media controller support - refine the vim2m-audio to support 8k<->16k conversion. changes in v8: - refine V4L2_CAP_AUDIO_M2M to be 0x00000008 - update doc for FIXED_POINT - address comments for imx-asrc changes in v7: - add acked-by from Mark - separate commit for fixed point, m2m audio class, audio rate controls - use INTEGER_MENU for rate, FIXED_POINT for rate offset - remove used fmts - address other comments for Hans changes in v6: - use m2m_prepare/m2m_unprepare/m2m_start/m2m_stop to replace m2m_start_part_one/m2m_stop_part_one, m2m_start_part_two/m2m_stop_part_two. - change V4L2_CTRL_TYPE_ASRC_RATE to V4L2_CTRL_TYPE_FIXED_POINT - fix warning by kernel test rebot - remove some unused format V4L2_AUDIO_FMT_XX - Get SNDRV_PCM_FORMAT from V4L2_AUDIO_FMT in driver. - rename audm2m to viaudm2m. changes in v5: - remove V4L2_AUDIO_FMT_LPCM - define audio pixel format like V4L2_AUDIO_FMT_S8... - remove rate and format in struct v4l2_audio_format. - Add V4L2_CID_ASRC_SOURCE_RATE and V4L2_CID_ASRC_DEST_RATE controls - updata document accordingly. changes in v4: - update document style - separate V4L2_AUDIO_FMT_LPCM and V4L2_CAP_AUDIO_M2M in separate commit changes in v3: - Modify documents for adding audio m2m support - Add audio virtual m2m driver - Defined V4L2_AUDIO_FMT_LPCM format type for audio. - Defined V4L2_CAP_AUDIO_M2M capability type for audio m2m case. - with modification in v4l-utils, pass v4l2-compliance test. changes in v2: - decouple the implementation in v4l2 and ALSA - implement the memory to memory driver as a platfrom driver and move it to driver/media - move fsl_asrc_common.h to include/sound folder Hans Verkuil (1): media: v4l2-ctrls: add support for fraction_bits Shengjiu Wang (15): ASoC: fsl_asrc: define functions for memory to memory usage ASoC: fsl_easrc: define functions for memory to memory usage ASoC: fsl_asrc: move fsl_asrc_common.h to include/sound ASoC: fsl_asrc: register m2m platform device ASoC: fsl_easrc: register m2m platform device media: uapi: Add V4L2_CAP_AUDIO_M2M capability flag media: v4l2: Add audio capture and output support media: uapi: Define audio sample format fourcc type media: uapi: Add V4L2_CTRL_CLASS_M2M_AUDIO media: uapi: Add audio rate controls support media: uapi: Declare interface types for Audio media: uapi: Add an entity type for audio resampler media: vivid: add fixed point test controls media: imx-asrc: Add memory to memory driver media: vim2m-audio: add virtual driver for audio memory to memory .../media/mediactl/media-types.rst | 11 + .../userspace-api/media/v4l/buffer.rst | 6 + .../userspace-api/media/v4l/common.rst | 1 + .../media/v4l/dev-audio-mem2mem.rst | 71 + .../userspace-api/media/v4l/devices.rst | 1 + .../media/v4l/ext-ctrls-audio-m2m.rst | 59 + .../userspace-api/media/v4l/pixfmt-audio.rst | 100 ++ .../userspace-api/media/v4l/pixfmt.rst | 1 + .../media/v4l/vidioc-enum-fmt.rst | 2 + .../media/v4l/vidioc-g-ext-ctrls.rst | 4 + .../userspace-api/media/v4l/vidioc-g-fmt.rst | 4 + .../media/v4l/vidioc-querycap.rst | 3 + .../media/v4l/vidioc-queryctrl.rst | 11 +- .../media/videodev2.h.rst.exceptions | 3 + MAINTAINERS | 17 + .../media/common/videobuf2/videobuf2-v4l2.c | 4 + drivers/media/platform/nxp/Kconfig | 13 + drivers/media/platform/nxp/Makefile | 1 + drivers/media/platform/nxp/imx-asrc.c | 1256 +++++++++++++++++ drivers/media/test-drivers/Kconfig | 10 + drivers/media/test-drivers/Makefile | 1 + drivers/media/test-drivers/vim2m-audio.c | 793 +++++++++++ drivers/media/test-drivers/vivid/vivid-core.h | 2 + .../media/test-drivers/vivid/vivid-ctrls.c | 26 + drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 9 + drivers/media/v4l2-core/v4l2-ctrls-api.c | 1 + drivers/media/v4l2-core/v4l2-ctrls-core.c | 93 +- drivers/media/v4l2-core/v4l2-ctrls-defs.c | 10 + drivers/media/v4l2-core/v4l2-dev.c | 21 + drivers/media/v4l2-core/v4l2-ioctl.c | 66 + drivers/media/v4l2-core/v4l2-mem2mem.c | 13 +- include/media/v4l2-ctrls.h | 13 +- include/media/v4l2-dev.h | 2 + include/media/v4l2-ioctl.h | 34 + .../fsl => include/sound}/fsl_asrc_common.h | 60 + include/uapi/linux/media.h | 2 + include/uapi/linux/v4l2-controls.h | 9 + include/uapi/linux/videodev2.h | 50 +- sound/soc/fsl/fsl_asrc.c | 144 ++ sound/soc/fsl/fsl_asrc.h | 4 +- sound/soc/fsl/fsl_asrc_dma.c | 2 +- sound/soc/fsl/fsl_easrc.c | 233 +++ sound/soc/fsl/fsl_easrc.h | 6 +- 43 files changed, 3145 insertions(+), 27 deletions(-) create mode 100644 Documentation/userspace-api/media/v4l/dev-audio-mem2mem.rst create mode 100644 Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-audio.rst create mode 100644 drivers/media/platform/nxp/imx-asrc.c create mode 100644 drivers/media/test-drivers/vim2m-audio.c rename {sound/soc/fsl => include/sound}/fsl_asrc_common.h (60%) -- 2.34.1
Move fsl_asrc_common.h to include/sound that it can be included from other drivers. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Acked-by: Mark Brown <broonie@kernel.org> --- {sound/soc/fsl => include/sound}/fsl_asrc_common.h | 0 sound/soc/fsl/fsl_asrc.h | 2 +- sound/soc/fsl/fsl_asrc_dma.c | 2 +- sound/soc/fsl/fsl_easrc.h | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename {sound/soc/fsl => include/sound}/fsl_asrc_common.h (100%) diff --git a/sound/soc/fsl/fsl_asrc_common.h b/include/sound/fsl_asrc_common.h similarity index 100% rename from sound/soc/fsl/fsl_asrc_common.h rename to include/sound/fsl_asrc_common.h diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 1c492eb237f5..66544624de7b 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -10,7 +10,7 @@ #ifndef _FSL_ASRC_H #define _FSL_ASRC_H -#include "fsl_asrc_common.h" +#include <sound/fsl_asrc_common.h> #define ASRC_M2M_INPUTFIFO_WML 0x4 #define ASRC_M2M_OUTPUTFIFO_WML 0x2 diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index f501f47242fb..f067bf1ecea7 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -12,7 +12,7 @@ #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> -#include "fsl_asrc_common.h" +#include <sound/fsl_asrc_common.h> #define FSL_ASRC_DMABUF_SIZE (256 * 1024) diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index c9f770862662..a24e540876a4 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -9,7 +9,7 @@ #include <sound/asound.h> #include <linux/dma/imx-dma.h> -#include "fsl_asrc_common.h" +#include <sound/fsl_asrc_common.h> /* EASRC Register Map */ -- 2.34.1
Audio memory to memory virtual driver use video memory to memory virtual driver vim2m.c as example. The main difference is device type is VFL_TYPE_AUDIO and device cap type is V4L2_CAP_AUDIO_M2M. The device_run function is a dummy function, which is simply copy the data from input buffer to output buffer. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- MAINTAINERS | 9 + drivers/media/test-drivers/Kconfig | 10 + drivers/media/test-drivers/Makefile | 1 + drivers/media/test-drivers/vim2m-audio.c | 793 +++++++++++++++++++++++ 4 files changed, 813 insertions(+) create mode 100644 drivers/media/test-drivers/vim2m-audio.c diff --git a/MAINTAINERS b/MAINTAINERS index 7b8b9ee65c61..215d40d80508 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23575,6 +23575,15 @@ L: linux-fsdevel@vger.kernel.org S: Maintained F: fs/vboxsf/* +VIRTUAL MEM2MEM DRIVER FOR AUDIO +M: Hans Verkuil <hverkuil@xs4all.nl> +M: Shengjiu Wang <shengjiu.wang@gmail.com> +L: linux-media@vger.kernel.org +S: Maintained +W: https://linuxtv.org +T: git git://linuxtv.org/media_tree.git +F: drivers/media/test-drivers/vim2m-audio.c + VIRTUAL PCM TEST DRIVER M: Ivan Orlov <ivan.orlov0322@gmail.com> L: linux-sound@vger.kernel.org diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig index 5a5379524bde..b6b52a7ca042 100644 --- a/drivers/media/test-drivers/Kconfig +++ b/drivers/media/test-drivers/Kconfig @@ -16,6 +16,16 @@ config VIDEO_VIM2M This is a virtual test device for the memory-to-memory driver framework. +config VIDEO_VIM2M_AUDIO + tristate "Virtual Memory-to-Memory Driver For Audio" + depends on VIDEO_DEV + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select MEDIA_CONTROLLER + help + This is a virtual audio test device for the memory-to-memory driver + framework. + source "drivers/media/test-drivers/vicodec/Kconfig" source "drivers/media/test-drivers/vimc/Kconfig" source "drivers/media/test-drivers/vivid/Kconfig" diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile index 740714a4584d..0c61c9ada3e1 100644 --- a/drivers/media/test-drivers/Makefile +++ b/drivers/media/test-drivers/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_DVB_VIDTV) += vidtv/ obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o +obj-$(CONFIG_VIDEO_VIM2M_AUDIO) += vim2m-audio.o obj-$(CONFIG_VIDEO_VIMC) += vimc/ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VISL) += visl/ diff --git a/drivers/media/test-drivers/vim2m-audio.c b/drivers/media/test-drivers/vim2m-audio.c new file mode 100644 index 000000000000..6361df6320b3 --- /dev/null +++ b/drivers/media/test-drivers/vim2m-audio.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A virtual v4l2-mem2mem example for audio device. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-vmalloc.h> +#include <sound/dmaengine_pcm.h> + +MODULE_DESCRIPTION("Virtual device for audio mem2mem testing"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "debug level"); + +#define MEM2MEM_NAME "vim2m-audio" + +#define dprintk(dev, lvl, fmt, arg...) \ + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) + +#define SAMPLE_NUM 4096 + +static void audm2m_dev_release(struct device *dev) +{} + +static struct platform_device audm2m_pdev = { + .name = MEM2MEM_NAME, + .dev.release = audm2m_dev_release, +}; + +static u32 formats[] = { + V4L2_AUDIO_FMT_S16_LE, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +/* Per-queue, driver-specific private data */ +struct audm2m_q_data { + unsigned int rate; + unsigned int channels; + unsigned int buffersize; + unsigned int sequence; + u32 fourcc; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +static snd_pcm_format_t find_format(u32 fourcc) +{ + snd_pcm_format_t fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + if (formats[k] == fourcc) + break; + } + + if (k == NUM_FORMATS) + return 0; + + fmt = v4l2_fourcc_to_audfmt(formats[k]); + + return fmt; +} + +struct audm2m_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + + struct mutex dev_mutex; + + struct v4l2_m2m_dev *m2m_dev; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif +}; + +struct audm2m_ctx { + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct audm2m_dev *dev; + + struct mutex vb_mutex; + + /* Source and destination queue data */ + struct audm2m_q_data q_data[2]; +}; + +static inline struct audm2m_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct audm2m_ctx, fh); +} + +static struct audm2m_q_data *get_q_data(struct audm2m_ctx *ctx, + enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT) + return &ctx->q_data[V4L2_M2M_SRC]; + return &ctx->q_data[V4L2_M2M_DST]; +} + +static const char *type_name(enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT) + return "Output"; + return "Capture"; +} + +/* + * mem2mem callbacks + */ + +/* + * device_run() - prepares and starts the device + */ +static void device_run(void *priv) +{ + struct audm2m_ctx *ctx = priv; + struct audm2m_dev *audm2m_dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct audm2m_q_data *q_data_src, *q_data_dst; + int src_size, dst_size = 0; + short *src_addr, *dst_addr; + int i; + + audm2m_dev = ctx->dev; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_OUTPUT); + if (!q_data_src) + return; + + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_CAPTURE); + if (!q_data_dst) + return; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + src_buf->sequence = q_data_src->sequence++; + dst_buf->sequence = q_data_dst->sequence++; + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + + /* Process the conversion */ + src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + src_addr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + dst_addr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + + if (q_data_src->rate == q_data_dst->rate) { + memcpy(dst_addr, src_addr, src_size); + dst_size = src_size; + } else if (q_data_src->rate == 2 * q_data_dst->rate) { + /* 8k to 16k */ + for (i = 0; i < src_size / 2; i++) { + *dst_addr++ = *src_addr++; + src_addr++; + } + + dst_size = src_size / 2; + } else if (q_data_src->rate * 2 == q_data_dst->rate) { + /* 16k to 8k */ + for (i = 0; i < src_size / 2; i++) { + *dst_addr++ = *src_addr; + *dst_addr++ = *src_addr++; + } + + dst_size = src_size * 2; + } + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_size); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_job_finish(audm2m_dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static int audm2m_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); + strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); + + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f) +{ + int i, num; + + num = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (num == f->index) + break; + /* + * Correct type but haven't reached our index yet, + * just increment per-type index + */ + ++num; + } + + if (i < NUM_FORMATS) { + /* Format found */ + f->pixelformat = formats[i]; + return 0; + } + + /* Format not found */ + return -EINVAL; +} + +static int audm2m_enum_fmt_audio_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f); +} + +static int audm2m_enum_fmt_audio_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f); +} + +static int audm2m_g_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct audm2m_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + f->fmt.audio.audioformat = q_data->fourcc; + f->fmt.audio.channels = q_data->channels; + f->fmt.audio.buffersize = q_data->buffersize; + + return 0; +} + +static int audm2m_g_fmt_audio_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return audm2m_g_fmt(file2ctx(file), f); +} + +static int audm2m_g_fmt_audio_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return audm2m_g_fmt(file2ctx(file), f); +} + +static int audm2m_try_fmt(struct v4l2_format *f, snd_pcm_format_t fmt) +{ + f->fmt.audio.channels = 1; + f->fmt.audio.buffersize = f->fmt.audio.channels * + snd_pcm_format_physical_width(fmt) * + SAMPLE_NUM; + return 0; +} + +static int audm2m_try_fmt_audio_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + snd_pcm_format_t fmt; + + fmt = find_format(f->fmt.audio.audioformat); + if (!fmt) { + f->fmt.audio.audioformat = formats[0]; + fmt = find_format(f->fmt.audio.audioformat); + } + + return audm2m_try_fmt(f, fmt); +} + +static int audm2m_try_fmt_audio_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + snd_pcm_format_t fmt; + + fmt = find_format(f->fmt.audio.audioformat); + if (!fmt) { + f->fmt.audio.audioformat = formats[0]; + fmt = find_format(f->fmt.audio.audioformat); + } + + return audm2m_try_fmt(f, fmt); +} + +static int audm2m_s_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f) +{ + struct audm2m_q_data *q_data; + struct vb2_queue *vq; + snd_pcm_format_t fmt; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + q_data->fourcc = f->fmt.audio.audioformat; + q_data->channels = f->fmt.audio.channels; + + fmt = find_format(f->fmt.audio.audioformat); + q_data->buffersize = q_data->channels * + snd_pcm_format_physical_width(fmt) * + SAMPLE_NUM; + + dprintk(ctx->dev, 1, + "Format for type %s: %d/%d, fmt: %c%c%c%c\n", + type_name(f->type), q_data->rate, + q_data->channels, + (q_data->fourcc & 0xff), + (q_data->fourcc >> 8) & 0xff, + (q_data->fourcc >> 16) & 0xff, + (q_data->fourcc >> 24) & 0xff); + + return 0; +} + +static int audm2m_s_fmt_audio_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = audm2m_try_fmt_audio_cap(file, priv, f); + if (ret) + return ret; + + return audm2m_s_fmt(file2ctx(file), f); +} + +static int audm2m_s_fmt_audio_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = audm2m_try_fmt_audio_out(file, priv, f); + if (ret) + return ret; + + return audm2m_s_fmt(file2ctx(file), f); +} + +static const struct v4l2_ioctl_ops audm2m_ioctl_ops = { + .vidioc_querycap = audm2m_querycap, + + .vidioc_enum_fmt_audio_cap = audm2m_enum_fmt_audio_cap, + .vidioc_g_fmt_audio_cap = audm2m_g_fmt_audio_cap, + .vidioc_try_fmt_audio_cap = audm2m_try_fmt_audio_cap, + .vidioc_s_fmt_audio_cap = audm2m_s_fmt_audio_cap, + + .vidioc_enum_fmt_audio_out = audm2m_enum_fmt_audio_out, + .vidioc_g_fmt_audio_out = audm2m_g_fmt_audio_out, + .vidioc_try_fmt_audio_out = audm2m_try_fmt_audio_out, + .vidioc_s_fmt_audio_out = audm2m_s_fmt_audio_out, + + .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_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Queue operations + */ +static int audm2m_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct audm2m_ctx *ctx = vb2_get_drv_priv(vq); + struct audm2m_q_data *q_data; + + q_data = get_q_data(ctx, vq->type); + + if (*nplanes) + return sizes[0] < q_data->buffersize ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = q_data->buffersize; + + dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n", + type_name(vq->type), *nplanes, sizes[0]); + + return 0; +} + +static void audm2m_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct audm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int audm2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct audm2m_ctx *ctx = vb2_get_drv_priv(q); + struct audm2m_q_data *q_data = get_q_data(ctx, q->type); + + q_data->sequence = 0; + return 0; +} + +static void audm2m_stop_streaming(struct vb2_queue *q) +{ + struct audm2m_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + return; + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops audm2m_qops = { + .queue_setup = audm2m_queue_setup, + .buf_queue = audm2m_buf_queue, + .start_streaming = audm2m_start_streaming, + .stop_streaming = audm2m_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct audm2m_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_AUDIO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &audm2m_qops; + src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->vb_mutex; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_AUDIO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &audm2m_qops; + dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->vb_mutex; + + return vb2_queue_init(dst_vq); +} + +static const s64 audm2m_rates[] = { + 8000, 16000, +}; + +static int audm2m_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct audm2m_ctx *ctx = + container_of(ctrl->handler, struct audm2m_ctx, ctrl_handler); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_M2M_AUDIO_SOURCE_RATE: + ctx->q_data[V4L2_M2M_SRC].rate = ctrl->qmenu_int[ctrl->val]; + break; + case V4L2_CID_M2M_AUDIO_DEST_RATE: + ctx->q_data[V4L2_M2M_DST].rate = ctrl->qmenu_int[ctrl->val]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops audm2m_ctrl_ops = { + .s_ctrl = audm2m_op_s_ctrl, +}; + +/* + * File operations + */ +static int audm2m_open(struct file *file) +{ + struct audm2m_dev *dev = video_drvdata(file); + struct audm2m_ctx *ctx = NULL; + snd_pcm_format_t fmt; + int width; + int rc = 0; + + if (mutex_lock_interruptible(&dev->dev_mutex)) + return -ERESTARTSYS; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + rc = -ENOMEM; + goto open_unlock; + } + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + + ctx->q_data[V4L2_M2M_SRC].fourcc = formats[0]; + ctx->q_data[V4L2_M2M_SRC].rate = 8000; + ctx->q_data[V4L2_M2M_SRC].channels = 1; + + /* Fix to 4096 samples */ + fmt = find_format(formats[0]); + width = snd_pcm_format_physical_width(fmt); + ctx->q_data[V4L2_M2M_SRC].buffersize = SAMPLE_NUM * width; + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + + mutex_init(&ctx->vb_mutex); + + if (IS_ERR(ctx->fh.m2m_ctx)) { + rc = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + goto open_unlock; + } + + v4l2_fh_add(&ctx->fh); + + dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n", + ctx, ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 2); + + v4l2_ctrl_new_int_menu(&ctx->ctrl_handler, &audm2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_SOURCE_RATE, + ARRAY_SIZE(audm2m_rates) - 1, 0, audm2m_rates); + v4l2_ctrl_new_int_menu(&ctx->ctrl_handler, &audm2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_DEST_RATE, + ARRAY_SIZE(audm2m_rates) - 1, 0, audm2m_rates); + + if (ctx->ctrl_handler.error) { + rc = ctx->ctrl_handler.error; + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + goto err_ctrl_handler; + } + + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + mutex_unlock(&dev->dev_mutex); + + return 0; + +err_ctrl_handler: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +open_unlock: + mutex_unlock(&dev->dev_mutex); + return rc; +} + +static int audm2m_release(struct file *file) +{ + struct audm2m_dev *dev = video_drvdata(file); + struct audm2m_ctx *ctx = file2ctx(file); + + dprintk(dev, 1, "Releasing instance %p\n", ctx); + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + mutex_lock(&dev->dev_mutex); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + mutex_unlock(&dev->dev_mutex); + kfree(ctx); + + return 0; +} + +static void audm2m_device_release(struct video_device *vdev) +{ + struct audm2m_dev *dev = container_of(vdev, struct audm2m_dev, vfd); + + v4l2_device_unregister(&dev->v4l2_dev); + v4l2_m2m_release(dev->m2m_dev); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_cleanup(&dev->mdev); +#endif + kfree(dev); +} + +static const struct v4l2_file_operations audm2m_fops = { + .owner = THIS_MODULE, + .open = audm2m_open, + .release = audm2m_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device audm2m_videodev = { + .name = MEM2MEM_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &audm2m_fops, + .ioctl_ops = &audm2m_ioctl_ops, + .minor = -1, + .release = audm2m_device_release, + .device_caps = V4L2_CAP_AUDIO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, +}; + +static const struct media_device_ops audm2m_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static int audm2m_probe(struct platform_device *pdev) +{ + struct audm2m_dev *dev; + struct video_device *vfd; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + goto error_free; + + mutex_init(&dev->dev_mutex); + + dev->vfd = audm2m_videodev; + vfd = &dev->vfd; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + video_set_drvdata(vfd, dev); + platform_set_drvdata(pdev, dev); + + dev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(dev->m2m_dev); + dev->m2m_dev = NULL; + goto error_dev; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &pdev->dev; + strscpy(dev->mdev.model, MEM2MEM_NAME, sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->mdev.ops = &audm2m_media_ops; + dev->v4l2_dev.mdev = &dev->mdev; +#endif + + ret = video_register_device(vfd, VFL_TYPE_AUDIO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto error_m2m; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, + MEDIA_ENT_F_PROC_AUDIO_RESAMPLER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto error_v4l2; + } + + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); + goto error_m2m_mc; + } +#endif + + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/v4l-audio%d\n", vfd->num); + + return 0; + +#ifdef CONFIG_MEDIA_CONTROLLER +error_m2m_mc: + v4l2_m2m_unregister_media_controller(dev->m2m_dev); +#endif +error_v4l2: + video_unregister_device(&dev->vfd); + /* audm2m_device_release called by video_unregister_device to release various objects */ + return ret; +error_m2m: + v4l2_m2m_release(dev->m2m_dev); +error_dev: + v4l2_device_unregister(&dev->v4l2_dev); +error_free: + kfree(dev); + + return ret; +} + +static void audm2m_remove(struct platform_device *pdev) +{ + struct audm2m_dev *dev = platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->m2m_dev); +#endif + video_unregister_device(&dev->vfd); +} + +static struct platform_driver audm2m_pdrv = { + .probe = audm2m_probe, + .remove_new = audm2m_remove, + .driver = { + .name = MEM2MEM_NAME, + }, +}; + +static void __exit audm2m_exit(void) +{ + platform_driver_unregister(&audm2m_pdrv); + platform_device_unregister(&audm2m_pdev); +} + +static int __init audm2m_init(void) +{ + int ret; + + ret = platform_device_register(&audm2m_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&audm2m_pdrv); + if (ret) + platform_device_unregister(&audm2m_pdev); + + return ret; +} + +module_init(audm2m_init); +module_exit(audm2m_exit); -- 2.34.1
Implement the ASRC memory to memory function using the v4l2 framework, user can use this function with v4l2 ioctl interface. User send the output and capture buffer to driver and driver store the converted data to the capture buffer. This feature can be shared by ASRC and EASRC drivers Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- MAINTAINERS | 8 + drivers/media/platform/nxp/Kconfig | 13 + drivers/media/platform/nxp/Makefile | 1 + drivers/media/platform/nxp/imx-asrc.c | 1256 +++++++++++++++++++++++++ 4 files changed, 1278 insertions(+) create mode 100644 drivers/media/platform/nxp/imx-asrc.c diff --git a/MAINTAINERS b/MAINTAINERS index 375d34363777..7b8b9ee65c61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15821,6 +15821,14 @@ F: drivers/nvmem/ F: include/linux/nvmem-consumer.h F: include/linux/nvmem-provider.h +NXP ASRC V4L2 MEM2MEM DRIVERS +M: Shengjiu Wang <shengjiu.wang@gmail.com> +L: linux-media@vger.kernel.org +S: Maintained +W: https://linuxtv.org +T: git git://linuxtv.org/media_tree.git +F: drivers/media/platform/nxp/imx-asrc.c + NXP BLUETOOTH WIRELESS DRIVERS M: Amitkumar Karwar <amitkumar.karwar@nxp.com> M: Neeraj Kale <neeraj.sanjaykale@nxp.com> diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig index 40e3436669e2..8d0ca335601f 100644 --- a/drivers/media/platform/nxp/Kconfig +++ b/drivers/media/platform/nxp/Kconfig @@ -67,3 +67,16 @@ config VIDEO_MX2_EMMAPRP source "drivers/media/platform/nxp/dw100/Kconfig" source "drivers/media/platform/nxp/imx-jpeg/Kconfig" + +config VIDEO_IMX_ASRC + tristate "NXP i.MX ASRC M2M support" + depends on V4L_MEM2MEM_DRIVERS + depends on MEDIA_SUPPORT + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select MEDIA_CONTROLLER + help + Say Y if you want to add ASRC M2M support for NXP CPUs. + It is a complement for ASRC M2P and ASRC P2M features. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile index 4d90eb713652..1325675e34f5 100644 --- a/drivers/media/platform/nxp/Makefile +++ b/drivers/media/platform/nxp/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o +obj-$(CONFIG_VIDEO_IMX_ASRC) += imx-asrc.o diff --git a/drivers/media/platform/nxp/imx-asrc.c b/drivers/media/platform/nxp/imx-asrc.c new file mode 100644 index 000000000000..0c25a36199b1 --- /dev/null +++ b/drivers/media/platform/nxp/imx-asrc.c @@ -0,0 +1,1256 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2014-2016 Freescale Semiconductor, Inc. +// Copyright (C) 2019-2023 NXP +// +// Freescale ASRC Memory to Memory (M2M) driver + +#include <linux/dma/imx-dma.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <sound/dmaengine_pcm.h> +#include <sound/fsl_asrc_common.h> + +#define V4L_CAP OUT +#define V4L_OUT IN + +#define ASRC_xPUT_DMA_CALLBACK(dir) \ + (((dir) == V4L_OUT) ? asrc_input_dma_callback \ + : asrc_output_dma_callback) + +#define DIR_STR(dir) (dir) == V4L_OUT ? "out" : "cap" + +/* Maximum output and capture buffer size */ +#define ASRC_M2M_BUFFER_SIZE (512 * 1024) + +/* Maximum output and capture period size */ +#define ASRC_M2M_PERIOD_SIZE (48 * 1024) + +struct asrc_pair_m2m { + struct fsl_asrc_pair *pair; + struct asrc_m2m *m2m; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + int channels[2]; + unsigned int sequence[2]; + s64 src_rate_off_prev; /* Q31.32 */ + s64 dst_rate_off_prev; /* Q31.32 */ + s64 src_rate_off_cur; /* Q31.32 */ + s64 dst_rate_off_cur; /* Q31.32 */ +}; + +struct asrc_m2m { + struct fsl_asrc_m2m_pdata pdata; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct video_device *dec_vdev; + struct mutex mlock; /* v4l2 ioctls serialization */ + struct platform_device *pdev; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif +}; + +static u32 formats[] = { + V4L2_AUDIO_FMT_S8, + V4L2_AUDIO_FMT_S16_LE, + V4L2_AUDIO_FMT_U16_LE, + V4L2_AUDIO_FMT_S24_LE, + V4L2_AUDIO_FMT_S24_3LE, + V4L2_AUDIO_FMT_U24_LE, + V4L2_AUDIO_FMT_U24_3LE, + V4L2_AUDIO_FMT_S32_LE, + V4L2_AUDIO_FMT_U32_LE, + V4L2_AUDIO_FMT_S20_3LE, + V4L2_AUDIO_FMT_U20_3LE, + V4L2_AUDIO_FMT_FLOAT_LE, + V4L2_AUDIO_FMT_IEC958_SUBFRAME_LE, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static const s64 asrc_v1_m2m_rates[] = { + 5512, 8000, 11025, 12000, 16000, + 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, + 128000, 176400, 192000, +}; + +static const s64 asrc_v2_m2m_rates[] = { + 8000, 11025, 12000, 16000, + 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, + 128000, 176400, 192000, 256000, + 352800, 384000, 705600, 768000, +}; + +static u32 find_fourcc(snd_pcm_format_t format) +{ + snd_pcm_format_t fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = v4l2_fourcc_to_audfmt(formats[k]); + if (fmt == format) + return formats[k]; + } + + return 0; +} + +static snd_pcm_format_t find_format(u32 fourcc) +{ + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + if (formats[k] == fourcc) + return v4l2_fourcc_to_audfmt(formats[k]); + } + + return 0; +} + +static int asrc_check_format(struct asrc_pair_m2m *pair_m2m, u8 dir, u32 format) +{ + struct asrc_m2m *m2m = pair_m2m->m2m; + struct fsl_asrc_m2m_pdata *pdata = &m2m->pdata; + struct fsl_asrc_pair *pair = pair_m2m->pair; + snd_pcm_format_t fmt; + u64 format_bit = 0; + int i; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (formats[i] == format) { + fmt = v4l2_fourcc_to_audfmt(formats[i]); + format_bit = pcm_format_to_bits(fmt); + break; + } + } + + if (dir == IN && !(format_bit & pdata->fmt_in)) + return find_fourcc(pair->sample_format[V4L_OUT]); + if (dir == OUT && !(format_bit & pdata->fmt_out)) + return find_fourcc(pair->sample_format[V4L_CAP]); + + return format; +} + +static int asrc_check_channel(struct asrc_pair_m2m *pair_m2m, u8 dir, u32 channels) +{ + struct asrc_m2m *m2m = pair_m2m->m2m; + struct fsl_asrc_m2m_pdata *pdata = &m2m->pdata; + struct fsl_asrc_pair *pair = pair_m2m->pair; + + if (channels < pdata->chan_min || channels > pdata->chan_max) + return pair->channels; + + return channels; +} + +static inline struct asrc_pair_m2m *asrc_m2m_fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct asrc_pair_m2m, fh); +} + +/** + * asrc_read_last_fifo: read all the remaining data from FIFO + * @pair: Structure pointer of fsl_asrc_pair + * @dma_vaddr: virtual address of capture buffer + * @length: payload length of capture buffer + */ +static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 i, reg, size, t_size = 0, width; + u32 *reg32 = NULL; + u16 *reg16 = NULL; + u8 *reg24 = NULL; + + width = snd_pcm_format_physical_width(pair->sample_format[V4L_CAP]); + if (width == 32) + reg32 = dma_vaddr + *length; + else if (width == 16) + reg16 = dma_vaddr + *length; + else + reg24 = dma_vaddr + *length; +retry: + size = asrc->get_output_fifo_size(pair); + if (size + *length > ASRC_M2M_BUFFER_SIZE) + goto end; + + for (i = 0; i < size * pair->channels; i++) { + regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®); + if (reg32) { + *reg32++ = reg; + } else if (reg16) { + *reg16++ = (u16)reg; + } else { + *reg24++ = (u8)reg; + *reg24++ = (u8)(reg >> 8); + *reg24++ = (u8)(reg >> 16); + } + } + t_size += size; + + /* In case there is data left in FIFO */ + if (size) + goto retry; +end: + /* Update payload length */ + if (reg32) + *length += t_size * pair->channels * 4; + else if (reg16) + *length += t_size * pair->channels * 2; + else + *length += t_size * pair->channels * 3; +} + +static int asrc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct asrc_pair_m2m *pair_m2m = vb2_get_drv_priv(q); + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct asrc_m2m *m2m = pair_m2m->m2m; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &m2m->pdev->dev; + struct vb2_v4l2_buffer *buf; + bool request_flag = false; + int ret; + + dev_dbg(dev, "Start streaming pair=%p, %d\n", pair, q->type); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to power up asrc\n"); + goto err_pm_runtime; + } + + /* Request asrc pair/context */ + if (!pair->req_pair) { + /* flag for error handler of this function */ + request_flag = true; + + ret = asrc->request_pair(pair->channels, pair); + if (ret) { + dev_err(dev, "failed to request pair: %d\n", ret); + goto err_request_pair; + } + + ret = asrc->m2m_prepare(pair); + if (ret) { + dev_err(dev, "failed to start pair part one: %d\n", ret); + goto err_start_part_one; + } + + pair->req_pair = true; + } + + /* Request dma channels */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + pair_m2m->sequence[V4L_OUT] = 0; + pair->dma_chan[V4L_OUT] = asrc->get_dma_channel(pair, IN); + if (!pair->dma_chan[V4L_OUT]) { + dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel; + } + } else { + pair_m2m->sequence[V4L_CAP] = 0; + pair->dma_chan[V4L_CAP] = asrc->get_dma_channel(pair, OUT); + if (!pair->dma_chan[V4L_CAP]) { + dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel; + } + } + + v4l2_m2m_update_start_streaming_state(pair_m2m->fh.m2m_ctx, q); + + return 0; + +err_dma_channel: + if (request_flag && asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); +err_start_part_one: + if (request_flag) + asrc->release_pair(pair); +err_request_pair: + pm_runtime_put_sync(dev); +err_pm_runtime: + /* Release buffers */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + while ((buf = v4l2_m2m_src_buf_remove(pair_m2m->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(pair_m2m->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } + return ret; +} + +static void asrc_m2m_stop_streaming(struct vb2_queue *q) +{ + struct asrc_pair_m2m *pair_m2m = vb2_get_drv_priv(q); + struct asrc_m2m *m2m = pair_m2m->m2m; + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &m2m->pdev->dev; + + dev_dbg(dev, "Stop streaming pair=%p, %d\n", pair, q->type); + + v4l2_m2m_update_stop_streaming_state(pair_m2m->fh.m2m_ctx, q); + + /* Stop & release pair/context */ + if (asrc->m2m_stop) + asrc->m2m_stop(pair); + + if (pair->req_pair) { + if (asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); + asrc->release_pair(pair); + pair->req_pair = false; + } + + /* Release dma channel */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (pair->dma_chan[V4L_OUT]) + dma_release_channel(pair->dma_chan[V4L_OUT]); + } else { + if (pair->dma_chan[V4L_CAP]) + dma_release_channel(pair->dma_chan[V4L_CAP]); + } + + pm_runtime_put_sync(dev); +} + +static int asrc_m2m_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct asrc_pair_m2m *pair_m2m = vb2_get_drv_priv(q); + struct fsl_asrc_pair *pair = pair_m2m->pair; + u32 size; + + /* + * The capture buffer size depends on output buffer size + * and the convert ratio. + * + * Here just use a fix length for capture and output buffer. + * User need to care about it. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) + size = pair->buf_len[V4L_OUT]; + else + size = pair->buf_len[V4L_CAP]; + + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + + *num_planes = 1; + sizes[0] = size; + + return 0; +} + +static void asrc_m2m_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct asrc_pair_m2m *pair_m2m = vb2_get_drv_priv(vb->vb2_queue); + + /* queue buffer */ + v4l2_m2m_buf_queue(pair_m2m->fh.m2m_ctx, vbuf); +} + +static const struct vb2_ops asrc_m2m_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = asrc_m2m_start_streaming, + .stop_streaming = asrc_m2m_stop_streaming, + .queue_setup = asrc_m2m_queue_setup, + .buf_queue = asrc_m2m_buf_queue, +}; + +/* Init video buffer queue for src and dst. */ +static int asrc_m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct asrc_pair_m2m *pair_m2m = priv; + struct asrc_m2m *m2m = pair_m2m->m2m; + int ret; + + src_vq->type = V4L2_BUF_TYPE_AUDIO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = pair_m2m; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &asrc_m2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &m2m->mlock; + src_vq->dev = &m2m->pdev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_AUDIO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = pair_m2m; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &asrc_m2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &m2m->mlock; + dst_vq->dev = &m2m->pdev->dev; + + ret = vb2_queue_init(dst_vq); + return ret; +} + +static int asrc_m2m_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct asrc_pair_m2m *pair_m2m = + container_of(ctrl->handler, struct asrc_pair_m2m, ctrl_handler); + struct fsl_asrc_pair *pair = pair_m2m->pair; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_M2M_AUDIO_SOURCE_RATE: + pair->rate[V4L_OUT] = ctrl->qmenu_int[ctrl->val]; + break; + case V4L2_CID_M2M_AUDIO_DEST_RATE: + pair->rate[V4L_CAP] = ctrl->qmenu_int[ctrl->val]; + break; + case V4L2_CID_M2M_AUDIO_SOURCE_RATE_OFFSET: + pair_m2m->src_rate_off_cur = *ctrl->p_new.p_s64; + break; + case V4L2_CID_M2M_AUDIO_DEST_RATE_OFFSET: + pair_m2m->dst_rate_off_cur = *ctrl->p_new.p_s64; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops asrc_m2m_ctrl_ops = { + .s_ctrl = asrc_m2m_op_s_ctrl, +}; + +static const struct v4l2_ctrl_config asrc_src_rate_off_control = { + .ops = &asrc_m2m_ctrl_ops, + .id = V4L2_CID_M2M_AUDIO_SOURCE_RATE_OFFSET, + .name = "Audio Source Sample Rate Offset", + .type = V4L2_CTRL_TYPE_INTEGER64, + .min = v4l2_ctrl_fp_compose(-128, 0, 32), + .max = v4l2_ctrl_fp_compose(127, 0xffffffff, 32), + .def = 0, + .step = 1, + .fraction_bits = 32, +}; + +static const struct v4l2_ctrl_config asrc_dst_rate_off_control = { + .ops = &asrc_m2m_ctrl_ops, + .id = V4L2_CID_M2M_AUDIO_DEST_RATE_OFFSET, + .name = "Audio Dest Sample Rate Offset", + .type = V4L2_CTRL_TYPE_INTEGER64, + .min = v4l2_ctrl_fp_compose(-128, 0, 32), + .max = v4l2_ctrl_fp_compose(127, 0xffffffff, 32), + .def = 0, + .step = 1, + .fraction_bits = 32, +}; + +/* system callback for open() */ +static int asrc_m2m_open(struct file *file) +{ + struct asrc_m2m *m2m = video_drvdata(file); + struct fsl_asrc *asrc = m2m->pdata.asrc; + struct video_device *vdev = video_devdata(file); + struct fsl_asrc_pair *pair; + struct asrc_pair_m2m *pair_m2m; + int ret = 0; + + if (mutex_lock_interruptible(&m2m->mlock)) + return -ERESTARTSYS; + + pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL); + if (!pair) { + ret = -ENOMEM; + goto err_alloc_pair; + } + + pair_m2m = kzalloc(sizeof(*pair_m2m), GFP_KERNEL); + if (!pair_m2m) { + ret = -ENOMEM; + goto err_alloc_pair_m2m; + } + + pair->private = (void *)pair + sizeof(struct fsl_asrc_pair); + pair->asrc = asrc; + + pair->buf_len[V4L_OUT] = ASRC_M2M_BUFFER_SIZE; + pair->buf_len[V4L_CAP] = ASRC_M2M_BUFFER_SIZE; + + pair->channels = 2; + pair->rate[V4L_OUT] = 8000; + pair->rate[V4L_CAP] = 8000; + pair->sample_format[V4L_OUT] = SNDRV_PCM_FORMAT_S16_LE; + pair->sample_format[V4L_CAP] = SNDRV_PCM_FORMAT_S16_LE; + + init_completion(&pair->complete[V4L_OUT]); + init_completion(&pair->complete[V4L_CAP]); + + v4l2_fh_init(&pair_m2m->fh, vdev); + v4l2_fh_add(&pair_m2m->fh); + file->private_data = &pair_m2m->fh; + + pair_m2m->pair = pair; + pair_m2m->m2m = m2m; + /* m2m context init */ + pair_m2m->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, pair_m2m, + asrc_m2m_queue_init); + if (IS_ERR(pair_m2m->fh.m2m_ctx)) { + ret = PTR_ERR(pair_m2m->fh.m2m_ctx); + goto err_ctx_init; + } + + v4l2_ctrl_handler_init(&pair_m2m->ctrl_handler, 4); + + if (m2m->pdata.rate_min == 5512) { + v4l2_ctrl_new_int_menu(&pair_m2m->ctrl_handler, &asrc_m2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_SOURCE_RATE, + ARRAY_SIZE(asrc_v1_m2m_rates) - 1, 1, asrc_v1_m2m_rates); + v4l2_ctrl_new_int_menu(&pair_m2m->ctrl_handler, &asrc_m2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_DEST_RATE, + ARRAY_SIZE(asrc_v1_m2m_rates) - 1, 1, asrc_v1_m2m_rates); + } else { + v4l2_ctrl_new_int_menu(&pair_m2m->ctrl_handler, &asrc_m2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_SOURCE_RATE, + ARRAY_SIZE(asrc_v2_m2m_rates) - 1, 0, asrc_v2_m2m_rates); + v4l2_ctrl_new_int_menu(&pair_m2m->ctrl_handler, &asrc_m2m_ctrl_ops, + V4L2_CID_M2M_AUDIO_DEST_RATE, + ARRAY_SIZE(asrc_v2_m2m_rates) - 1, 0, asrc_v2_m2m_rates); + } + + v4l2_ctrl_new_custom(&pair_m2m->ctrl_handler, &asrc_src_rate_off_control, NULL); + v4l2_ctrl_new_custom(&pair_m2m->ctrl_handler, &asrc_dst_rate_off_control, NULL); + + if (pair_m2m->ctrl_handler.error) { + ret = pair_m2m->ctrl_handler.error; + v4l2_ctrl_handler_free(&pair_m2m->ctrl_handler); + goto err_ctrl_handler; + } + + pair_m2m->fh.ctrl_handler = &pair_m2m->ctrl_handler; + + mutex_unlock(&m2m->mlock); + + return 0; + +err_ctrl_handler: + v4l2_m2m_ctx_release(pair_m2m->fh.m2m_ctx); +err_ctx_init: + v4l2_fh_del(&pair_m2m->fh); + v4l2_fh_exit(&pair_m2m->fh); + kfree(pair_m2m); +err_alloc_pair_m2m: + kfree(pair); +err_alloc_pair: + mutex_unlock(&m2m->mlock); + return ret; +} + +static int asrc_m2m_release(struct file *file) +{ + struct asrc_m2m *m2m = video_drvdata(file); + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(file->private_data); + struct fsl_asrc_pair *pair = pair_m2m->pair; + + mutex_lock(&m2m->mlock); + v4l2_ctrl_handler_free(&pair_m2m->ctrl_handler); + v4l2_m2m_ctx_release(pair_m2m->fh.m2m_ctx); + v4l2_fh_del(&pair_m2m->fh); + v4l2_fh_exit(&pair_m2m->fh); + kfree(pair_m2m); + kfree(pair); + mutex_unlock(&m2m->mlock); + + return 0; +} + +static const struct v4l2_file_operations asrc_m2m_fops = { + .owner = THIS_MODULE, + .open = asrc_m2m_open, + .release = asrc_m2m_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int asrc_m2m_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, M2M_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, M2M_DRV_NAME, sizeof(cap->card)); + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_AUDIO_M2M; + + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, u64 fmtbit) +{ + snd_pcm_format_t fmt; + int i, num; + + num = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + fmt = v4l2_fourcc_to_audfmt(formats[i]); + if (pcm_format_to_bits(fmt) & fmtbit) { + if (num == f->index) + break; + /* + * Correct type but haven't reached our index yet, + * just increment per-type index + */ + ++num; + } + } + + if (i < NUM_FORMATS) { + /* Format found */ + f->pixelformat = formats[i]; + return 0; + } + + return -EINVAL; +} + +static int asrc_m2m_enum_fmt_aud_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct asrc_m2m *m2m = pair_m2m->m2m; + + return enum_fmt(f, m2m->pdata.fmt_out); +} + +static int asrc_m2m_enum_fmt_aud_out(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct asrc_m2m *m2m = pair_m2m->m2m; + + return enum_fmt(f, m2m->pdata.fmt_in); +} + +static int asrc_m2m_g_fmt_aud_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct fsl_asrc_pair *pair = pair_m2m->pair; + + f->fmt.audio.channels = pair->channels; + f->fmt.audio.buffersize = pair->buf_len[V4L_CAP]; + f->fmt.audio.audioformat = find_fourcc(pair->sample_format[V4L_CAP]); + + return 0; +} + +static int asrc_m2m_g_fmt_aud_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct fsl_asrc_pair *pair = pair_m2m->pair; + + f->fmt.audio.channels = pair->channels; + f->fmt.audio.buffersize = pair->buf_len[V4L_OUT]; + f->fmt.audio.audioformat = find_fourcc(pair->sample_format[V4L_OUT]); + + return 0; +} + +/* output for asrc */ +static int asrc_m2m_s_fmt_aud_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct asrc_m2m *m2m = pair_m2m->m2m; + struct device *dev = &m2m->pdev->dev; + + f->fmt.audio.audioformat = asrc_check_format(pair_m2m, OUT, f->fmt.audio.audioformat); + f->fmt.audio.channels = asrc_check_channel(pair_m2m, OUT, f->fmt.audio.channels); + + if (pair_m2m->channels[V4L_CAP] > 0 && + pair_m2m->channels[V4L_CAP] != f->fmt.audio.channels) { + dev_err(dev, "channels don't match for cap and out\n"); + return -EINVAL; + } + + pair_m2m->channels[V4L_CAP] = f->fmt.audio.channels; + pair->channels = f->fmt.audio.channels; + pair->sample_format[V4L_CAP] = find_format(f->fmt.audio.audioformat); + + return 0; +} + +/* input for asrc */ +static int asrc_m2m_s_fmt_aud_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct asrc_m2m *m2m = pair_m2m->m2m; + struct device *dev = &m2m->pdev->dev; + + f->fmt.audio.audioformat = asrc_check_format(pair_m2m, IN, f->fmt.audio.audioformat); + f->fmt.audio.channels = asrc_check_channel(pair_m2m, IN, f->fmt.audio.channels); + if (pair_m2m->channels[V4L_OUT] > 0 && + pair_m2m->channels[V4L_OUT] != f->fmt.audio.channels) { + dev_err(dev, "channels don't match for cap and out\n"); + return -EINVAL; + } + + pair_m2m->channels[V4L_OUT] = f->fmt.audio.channels; + pair->channels = f->fmt.audio.channels; + pair->sample_format[V4L_OUT] = find_format(f->fmt.audio.audioformat); + + return 0; +} + +static int asrc_m2m_try_fmt_audio_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + + f->fmt.audio.audioformat = asrc_check_format(pair_m2m, OUT, f->fmt.audio.audioformat); + f->fmt.audio.channels = asrc_check_channel(pair_m2m, OUT, f->fmt.audio.channels); + + return 0; +} + +static int asrc_m2m_try_fmt_audio_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct asrc_pair_m2m *pair_m2m = asrc_m2m_fh_to_ctx(fh); + + f->fmt.audio.audioformat = asrc_check_format(pair_m2m, IN, f->fmt.audio.audioformat); + f->fmt.audio.channels = asrc_check_channel(pair_m2m, IN, f->fmt.audio.channels); + + return 0; +} + +static const struct v4l2_ioctl_ops asrc_m2m_ioctl_ops = { + .vidioc_querycap = asrc_m2m_querycap, + + .vidioc_enum_fmt_audio_cap = asrc_m2m_enum_fmt_aud_cap, + .vidioc_enum_fmt_audio_out = asrc_m2m_enum_fmt_aud_out, + + .vidioc_g_fmt_audio_cap = asrc_m2m_g_fmt_aud_cap, + .vidioc_g_fmt_audio_out = asrc_m2m_g_fmt_aud_out, + + .vidioc_s_fmt_audio_cap = asrc_m2m_s_fmt_aud_cap, + .vidioc_s_fmt_audio_out = asrc_m2m_s_fmt_aud_out, + + .vidioc_try_fmt_audio_cap = asrc_m2m_try_fmt_audio_cap, + .vidioc_try_fmt_audio_out = asrc_m2m_try_fmt_audio_out, + + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* dma complete callback */ +static void asrc_input_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[V4L_OUT]); +} + +/* dma complete callback */ +static void asrc_output_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[V4L_CAP]); +} + +/* config dma channel */ +static int asrc_dmaconfig(struct asrc_pair_m2m *pair_m2m, + struct dma_chan *chan, + u32 dma_addr, dma_addr_t buf_addr, u32 buf_len, + int dir, int width) +{ + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct fsl_asrc *asrc = pair->asrc; + struct asrc_m2m *m2m = pair_m2m->m2m; + struct device *dev = &m2m->pdev->dev; + struct dma_slave_config slave_config; + enum dma_slave_buswidth buswidth; + unsigned int sg_len, max_period_size; + struct scatterlist *sg; + int ret, i; + + switch (width) { + case 8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 24: + buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dev, "invalid word width\n"); + return -EINVAL; + } + + memset(&slave_config, 0, sizeof(slave_config)); + if (dir == V4L_OUT) { + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = dma_addr; + slave_config.dst_addr_width = buswidth; + slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair); + } else { + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = dma_addr; + slave_config.src_addr_width = buswidth; + slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair); + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) { + dev_err(dev, "failed to config dmaengine for %s task: %d\n", + DIR_STR(dir), ret); + return -EINVAL; + } + + max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8); + /* scatter gather mode */ + sg_len = buf_len / max_period_size; + if (buf_len % max_period_size) + sg_len += 1; + + sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL); + if (!sg) + return -ENOMEM; + + sg_init_table(sg, sg_len); + for (i = 0; i < (sg_len - 1); i++) { + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = max_period_size; + } + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = buf_len - i * max_period_size; + + pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len, + slave_config.direction, + DMA_PREP_INTERRUPT); + kfree(sg); + if (!pair->desc[dir]) { + dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir)); + return -EINVAL; + } + + pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir); + pair->desc[dir]->callback_param = pair; + + return 0; +} + +static void asrc_m2m_set_ratio_mod(struct asrc_pair_m2m *pair_m2m) +{ + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct fsl_asrc *asrc = pair->asrc; + s32 src_rate_int, dst_rate_int; + s64 src_rate_frac; + s64 dst_rate_frac; + u64 src_rate, dst_rate; + u64 ratio_pre, ratio_cur; + s64 ratio_diff; + + if (!asrc->m2m_set_ratio_mod) + return; + + if (pair_m2m->src_rate_off_cur == pair_m2m->src_rate_off_prev && + pair_m2m->dst_rate_off_cur == pair_m2m->dst_rate_off_prev) + return; + + /* + * use maximum rate 768kHz as limitation, then we can shift right 21 bit for + * division + */ + src_rate_int = pair->rate[V4L_OUT]; + src_rate_frac = pair_m2m->src_rate_off_prev; + + src_rate = ((s64)src_rate_int << 32) + src_rate_frac; + + dst_rate_int = pair->rate[V4L_CAP]; + dst_rate_frac = pair_m2m->dst_rate_off_prev; + + dst_rate = ((s64)dst_rate_int << 32) + dst_rate_frac; + dst_rate >>= 21; + do_div(src_rate, dst_rate); + ratio_pre = src_rate; + + src_rate_frac = pair_m2m->src_rate_off_cur; + src_rate = ((s64)src_rate_int << 32) + src_rate_frac; + + dst_rate_frac = pair_m2m->dst_rate_off_cur; + dst_rate = ((s64)dst_rate_int << 32) + dst_rate_frac; + dst_rate >>= 21; + do_div(src_rate, dst_rate); + ratio_cur = src_rate; + + ratio_diff = ratio_cur - ratio_pre; + asrc->m2m_set_ratio_mod(pair, ratio_diff << 10); + + pair_m2m->src_rate_off_prev = pair_m2m->src_rate_off_cur; + pair_m2m->dst_rate_off_prev = pair_m2m->dst_rate_off_cur; +} + +/* main function of converter */ +static void asrc_m2m_device_run(void *priv) +{ + struct asrc_pair_m2m *pair_m2m = priv; + struct fsl_asrc_pair *pair = pair_m2m->pair; + struct asrc_m2m *m2m = pair_m2m->m2m; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &m2m->pdev->dev; + enum asrc_pair_index index = pair->index; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned int out_buf_len; + unsigned int cap_dma_len; + unsigned int width; + u32 fifo_addr; + int ret; + + /* set ratio mod */ + asrc_m2m_set_ratio_mod(pair_m2m); + + src_buf = v4l2_m2m_next_src_buf(pair_m2m->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(pair_m2m->fh.m2m_ctx); + + src_buf->sequence = pair_m2m->sequence[V4L_OUT]++; + dst_buf->sequence = pair_m2m->sequence[V4L_CAP]++; + + width = snd_pcm_format_physical_width(pair->sample_format[V4L_OUT]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index); + out_buf_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + if (out_buf_len < width * pair->channels / 8 || + out_buf_len > ASRC_M2M_BUFFER_SIZE || + out_buf_len % (width * pair->channels / 8)) { + dev_err(dev, "out buffer size is error: [%d]\n", out_buf_len); + goto end; + } + + /* dma config for output dma channel */ + ret = asrc_dmaconfig(pair_m2m, + pair->dma_chan[V4L_OUT], + fifo_addr, + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0), + out_buf_len, V4L_OUT, width); + if (ret) { + dev_err(dev, "out dma config error\n"); + goto end; + } + + width = snd_pcm_format_physical_width(pair->sample_format[V4L_CAP]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index); + cap_dma_len = asrc->m2m_calc_out_len(pair, out_buf_len); + if (cap_dma_len > 0 && cap_dma_len <= ASRC_M2M_BUFFER_SIZE) { + /* dma config for capture dma channel */ + ret = asrc_dmaconfig(pair_m2m, + pair->dma_chan[V4L_CAP], + fifo_addr, + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), + cap_dma_len, V4L_CAP, width); + if (ret) { + dev_err(dev, "cap dma config error\n"); + goto end; + } + } else if (cap_dma_len > ASRC_M2M_BUFFER_SIZE) { + dev_err(dev, "cap buffer size error\n"); + goto end; + } + + reinit_completion(&pair->complete[V4L_OUT]); + reinit_completion(&pair->complete[V4L_CAP]); + + /* Submit DMA request */ + dmaengine_submit(pair->desc[V4L_OUT]); + dma_async_issue_pending(pair->desc[V4L_OUT]->chan); + if (cap_dma_len > 0) { + dmaengine_submit(pair->desc[V4L_CAP]); + dma_async_issue_pending(pair->desc[V4L_CAP]->chan); + } + + asrc->m2m_start(pair); + + if (!wait_for_completion_interruptible_timeout(&pair->complete[V4L_OUT], 10 * HZ)) { + dev_err(dev, "out DMA task timeout\n"); + goto end; + } + + if (cap_dma_len > 0) { + if (!wait_for_completion_interruptible_timeout(&pair->complete[V4L_CAP], 10 * HZ)) { + dev_err(dev, "cap DMA task timeout\n"); + goto end; + } + } + + /* read the last words from FIFO */ + asrc_read_last_fifo(pair, vb2_plane_vaddr(&dst_buf->vb2_buf, 0), &cap_dma_len); + /* update payload length for capture */ + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, cap_dma_len); + +end: + src_buf = v4l2_m2m_src_buf_remove(pair_m2m->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(pair_m2m->fh.m2m_ctx); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + v4l2_m2m_job_finish(m2m->m2m_dev, pair_m2m->fh.m2m_ctx); +} + +static int asrc_m2m_job_ready(void *priv) +{ + struct asrc_pair_m2m *pair_m2m = priv; + + if (v4l2_m2m_num_src_bufs_ready(pair_m2m->fh.m2m_ctx) > 0 && + v4l2_m2m_num_dst_bufs_ready(pair_m2m->fh.m2m_ctx) > 0) { + return 1; + } + + return 0; +} + +static const struct v4l2_m2m_ops asrc_m2m_ops = { + .job_ready = asrc_m2m_job_ready, + .device_run = asrc_m2m_device_run, +}; + +static const struct media_device_ops asrc_m2m_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static int asrc_m2m_probe(struct platform_device *pdev) +{ + struct fsl_asrc_m2m_pdata *data = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct asrc_m2m *m2m; + int ret; + + m2m = devm_kzalloc(dev, sizeof(*m2m), GFP_KERNEL); + if (!m2m) + return -ENOMEM; + + m2m->pdata = *data; + m2m->pdev = pdev; + + ret = v4l2_device_register(dev, &m2m->v4l2_dev); + if (ret) { + dev_err(dev, "failed to register v4l2 device\n"); + goto err_register; + } + + m2m->m2m_dev = v4l2_m2m_init(&asrc_m2m_ops); + if (IS_ERR(m2m->m2m_dev)) { + ret = PTR_ERR(m2m->m2m_dev); + dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + goto err_m2m; + } + + m2m->dec_vdev = video_device_alloc(); + if (!m2m->dec_vdev) { + ret = -ENOMEM; + goto err_vdev_alloc; + } + + mutex_init(&m2m->mlock); + + m2m->dec_vdev->fops = &asrc_m2m_fops; + m2m->dec_vdev->ioctl_ops = &asrc_m2m_ioctl_ops; + m2m->dec_vdev->minor = -1; + m2m->dec_vdev->release = video_device_release; + m2m->dec_vdev->lock = &m2m->mlock; /* lock for ioctl serialization */ + m2m->dec_vdev->v4l2_dev = &m2m->v4l2_dev; + m2m->dec_vdev->vfl_dir = VFL_DIR_M2M; + m2m->dec_vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_AUDIO_M2M; + +#ifdef CONFIG_MEDIA_CONTROLLER + m2m->mdev.dev = &pdev->dev; + strscpy(m2m->mdev.model, M2M_DRV_NAME, sizeof(m2m->mdev.model)); + media_device_init(&m2m->mdev); + m2m->mdev.ops = &asrc_m2m_media_ops; + m2m->v4l2_dev.mdev = &m2m->mdev; +#endif + + ret = video_register_device(m2m->dec_vdev, VFL_TYPE_AUDIO, -1); + if (ret) { + dev_err_probe(dev, ret, "failed to register video device\n"); + goto err_vdev_register; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = v4l2_m2m_register_media_controller(m2m->m2m_dev, m2m->dec_vdev, + MEDIA_ENT_F_PROC_AUDIO_RESAMPLER); + if (ret) { + dev_err_probe(dev, ret, "Failed to init mem2mem media controller\n"); + goto error_v4l2; + } + + ret = media_device_register(&m2m->mdev); + if (ret) { + dev_err_probe(dev, ret, "Failed to register mem2mem media device\n"); + goto error_m2m_mc; + } +#endif + + video_set_drvdata(m2m->dec_vdev, m2m); + platform_set_drvdata(pdev, m2m); + pm_runtime_enable(&pdev->dev); + + return 0; + +#ifdef CONFIG_MEDIA_CONTROLLER +error_m2m_mc: + v4l2_m2m_unregister_media_controller(m2m->m2m_dev); +#endif +error_v4l2: + video_unregister_device(m2m->dec_vdev); +err_vdev_register: + video_device_release(m2m->dec_vdev); +err_vdev_alloc: + v4l2_m2m_release(m2m->m2m_dev); +err_m2m: + v4l2_device_unregister(&m2m->v4l2_dev); +err_register: + return ret; +} + +static void asrc_m2m_remove(struct platform_device *pdev) +{ + struct asrc_m2m *m2m = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&m2m->mdev); + v4l2_m2m_unregister_media_controller(m2m->m2m_dev); +#endif + video_unregister_device(m2m->dec_vdev); + video_device_release(m2m->dec_vdev); + v4l2_m2m_release(m2m->m2m_dev); + v4l2_device_unregister(&m2m->v4l2_dev); +} + +#ifdef CONFIG_PM_SLEEP +/* suspend callback for m2m */ +static int asrc_m2m_suspend(struct device *dev) +{ + struct asrc_m2m *m2m = dev_get_drvdata(dev); + struct fsl_asrc *asrc = m2m->pdata.asrc; + struct fsl_asrc_pair *pair; + unsigned long lock_flags; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + spin_lock_irqsave(&asrc->lock, lock_flags); + pair = asrc->pair[i]; + if (!pair || !pair->req_pair) { + spin_unlock_irqrestore(&asrc->lock, lock_flags); + continue; + } + if (!completion_done(&pair->complete[V4L_OUT])) { + if (pair->dma_chan[V4L_OUT]) + dmaengine_terminate_all(pair->dma_chan[V4L_OUT]); + asrc_input_dma_callback((void *)pair); + } + if (!completion_done(&pair->complete[V4L_CAP])) { + if (pair->dma_chan[V4L_CAP]) + dmaengine_terminate_all(pair->dma_chan[V4L_CAP]); + asrc_output_dma_callback((void *)pair); + } + + if (asrc->m2m_pair_suspend) + asrc->m2m_pair_suspend(pair); + + spin_unlock_irqrestore(&asrc->lock, lock_flags); + } + + return 0; +} + +static int asrc_m2m_resume(struct device *dev) +{ + struct asrc_m2m *m2m = dev_get_drvdata(dev); + struct fsl_asrc *asrc = m2m->pdata.asrc; + struct fsl_asrc_pair *pair; + unsigned long lock_flags; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + spin_lock_irqsave(&asrc->lock, lock_flags); + pair = asrc->pair[i]; + if (!pair || !pair->req_pair) { + spin_unlock_irqrestore(&asrc->lock, lock_flags); + continue; + } + if (asrc->m2m_pair_resume) + asrc->m2m_pair_resume(pair); + + spin_unlock_irqrestore(&asrc->lock, lock_flags); + } + + return 0; +} +#endif + +static const struct dev_pm_ops asrc_m2m_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(asrc_m2m_suspend, + asrc_m2m_resume) +}; + +static const struct platform_device_id asrc_m2m_driver_ids[] __always_unused = { + { .name = M2M_DRV_NAME }, + { }, +}; +MODULE_DEVICE_TABLE(platform, asrc_m2m_driver_ids); + +static struct platform_driver asrc_m2m_driver = { + .probe = asrc_m2m_probe, + .remove_new = asrc_m2m_remove, + .id_table = asrc_m2m_driver_ids, + .driver = { + .name = M2M_DRV_NAME, + .pm = &asrc_m2m_pm_ops, + }, +}; +module_platform_driver(asrc_m2m_driver); + +MODULE_DESCRIPTION("Freescale ASRC M2M driver"); +MODULE_LICENSE("GPL"); -- 2.34.1
Add fixed point test controls, one is for Q4.16 format another one is for Q63 format. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- drivers/media/test-drivers/vivid/vivid-core.h | 2 ++ .../media/test-drivers/vivid/vivid-ctrls.c | 26 +++++++++++++++++++ include/media/v4l2-ctrls.h | 6 +++++ 3 files changed, 34 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index cfb8e66083f6..f65465191bc9 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -222,6 +222,8 @@ struct vivid_dev { struct v4l2_ctrl *boolean; struct v4l2_ctrl *int32; struct v4l2_ctrl *int64; + struct v4l2_ctrl *int32_q16; + struct v4l2_ctrl *int64_q63; struct v4l2_ctrl *menu; struct v4l2_ctrl *string; struct v4l2_ctrl *bitmask; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f2b20e25a7a4..2444ea95b285 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -38,6 +38,8 @@ #define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14) #define VIVID_CID_S32_ARRAY (VIVID_CID_CUSTOM_BASE + 15) #define VIVID_CID_S64_ARRAY (VIVID_CID_CUSTOM_BASE + 16) +#define VIVID_CID_INT_Q4_16 (VIVID_CID_CUSTOM_BASE + 17) +#define VIVID_CID_INT64_Q63 (VIVID_CID_CUSTOM_BASE + 18) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -182,6 +184,28 @@ static const struct v4l2_ctrl_config vivid_ctrl_int64 = { .step = 1, }; +static const struct v4l2_ctrl_config vivid_ctrl_int32_q16 = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_INT_Q4_16, + .name = "Integer 32 Bits Q4.16", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = v4l2_ctrl_fp_compose(-16, 0, 16), + .max = v4l2_ctrl_fp_compose(15, 0xffff, 16), + .step = 1, + .fraction_bits = 16, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_int64_q63 = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_INT64_Q63, + .name = "Integer 64 Bits Q63", + .type = V4L2_CTRL_TYPE_INTEGER64, + .min = v4l2_ctrl_fp_compose(-1, 0, 63), + .max = v4l2_ctrl_fp_compose(0, LLONG_MAX, 63), + .step = 1, + .fraction_bits = 63, +}; + static const struct v4l2_ctrl_config vivid_ctrl_u32_array = { .ops = &vivid_user_gen_ctrl_ops, .id = VIVID_CID_U32_ARRAY, @@ -1670,6 +1694,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->button = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_button, NULL); dev->int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32, NULL); dev->int64 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64, NULL); + dev->int32_q16 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int32_q16, NULL); + dev->int64_q63 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int64_q63, NULL); dev->boolean = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_boolean, NULL); dev->menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_menu, NULL); dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c35514c5bf88..197d8b67ac13 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -1593,4 +1593,10 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl); */ int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr); +/* + * Fixed point compose helper define. This helper maps to the value + * i + f / (1 << fraction_bits). + */ +#define v4l2_ctrl_fp_compose(i, f, fraction_bits) (((s64)(i) << fraction_bits) + (f)) + #endif -- 2.34.1
Add and document a media entity type for an audio resampler. It is MEDIA_ENT_F_PROC_AUDIO_RESAMPLER. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- Documentation/userspace-api/media/mediactl/media-types.rst | 6 ++++++ include/uapi/linux/media.h | 1 + 2 files changed, 7 insertions(+) diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index adfb37430f8e..d353f17c3344 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -40,6 +40,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-ENT-F-PROC-VIDEO-ENCODER: .. _MEDIA-ENT-F-PROC-VIDEO-DECODER: .. _MEDIA-ENT-F-PROC-VIDEO-ISP: +.. _MEDIA-ENT-F-PROC-AUDIO-RESAMPLER: .. _MEDIA-ENT-F-VID-MUX: .. _MEDIA-ENT-F-VID-IF-BRIDGE: .. _MEDIA-ENT-F-DV-DECODER: @@ -208,6 +209,11 @@ Types and flags used to represent the media graph elements combination of custom V4L2 controls and IOCTLs, and parameters supplied in a metadata buffer. + * - ``MEDIA_ENT_F_PROC_AUDIO_RESAMPLER`` + - An Audio Resampler device. An entity capable of + resampling an audio stream from one sample rate to another sample + rate. Must have one sink pad and at least one source pad. + * - ``MEDIA_ENT_F_VID_MUX`` - Video multiplexer. An entity capable of multiplexing must have at least two sink pads and one source pad, and must pass the video diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 9ff6dec7393a..a8266eaa8042 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -125,6 +125,7 @@ struct media_device_info { #define MEDIA_ENT_F_PROC_VIDEO_ENCODER (MEDIA_ENT_F_BASE + 0x4007) #define MEDIA_ENT_F_PROC_VIDEO_DECODER (MEDIA_ENT_F_BASE + 0x4008) #define MEDIA_ENT_F_PROC_VIDEO_ISP (MEDIA_ENT_F_BASE + 0x4009) +#define MEDIA_ENT_F_PROC_AUDIO_RESAMPLER (MEDIA_ENT_F_BASE + 0x400a) /* * Switch and bridge entity functions -- 2.34.1
Declare the interface types that will be used by Audio. The type is MEDIA_INTF_T_V4L_AUDIO. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- .../userspace-api/media/mediactl/media-types.rst | 5 +++++ drivers/media/v4l2-core/v4l2-dev.c | 4 ++++ drivers/media/v4l2-core/v4l2-mem2mem.c | 13 +++++++++---- include/uapi/linux/media.h | 1 + 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index 6332e8395263..adfb37430f8e 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -265,6 +265,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-INTF-T-V4L-SUBDEV: .. _MEDIA-INTF-T-V4L-SWRADIO: .. _MEDIA-INTF-T-V4L-TOUCH: +.. _MEDIA-INTF-T-V4L-AUDIO: .. _MEDIA-INTF-T-ALSA-PCM-CAPTURE: .. _MEDIA-INTF-T-ALSA-PCM-PLAYBACK: .. _MEDIA-INTF-T-ALSA-CONTROL: @@ -322,6 +323,10 @@ Types and flags used to represent the media graph elements - Device node interface for Touch device (V4L) - typically, /dev/v4l-touch? + * - ``MEDIA_INTF_T_V4L_AUDIO`` + - Device node interface for Audio device (V4L) + - typically, /dev/v4l-audio? + * - ``MEDIA_INTF_T_ALSA_PCM_CAPTURE`` - Device node interface for ALSA PCM Capture - typically, /dev/snd/pcmC?D?c diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index bac008fcedc6..ca8462a61e1f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -844,6 +844,10 @@ static int video_register_media_controller(struct video_device *vdev) intf_type = MEDIA_INTF_T_V4L_SUBDEV; /* Entity will be created via v4l2_device_register_subdev() */ break; + case VFL_TYPE_AUDIO: + intf_type = MEDIA_INTF_T_V4L_AUDIO; + /* Entity will be created via v4l2_device_register_subdev() */ + break; default: return 0; } diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 75517134a5e9..cda5e255305f 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -1143,10 +1143,15 @@ int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, if (ret) goto err_rm_links0; - /* Create video interface */ - m2m_dev->intf_devnode = media_devnode_create(mdev, - MEDIA_INTF_T_V4L_VIDEO, 0, - VIDEO_MAJOR, vdev->minor); + if (vdev->vfl_type == VFL_TYPE_AUDIO) + m2m_dev->intf_devnode = media_devnode_create(mdev, + MEDIA_INTF_T_V4L_AUDIO, 0, + VIDEO_MAJOR, vdev->minor); + else + /* Create video interface */ + m2m_dev->intf_devnode = media_devnode_create(mdev, + MEDIA_INTF_T_V4L_VIDEO, 0, + VIDEO_MAJOR, vdev->minor); if (!m2m_dev->intf_devnode) { ret = -ENOMEM; goto err_rm_links1; diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 1c80b1d6bbaf..9ff6dec7393a 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -260,6 +260,7 @@ struct media_links_enum { #define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3) #define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4) #define MEDIA_INTF_T_V4L_TOUCH (MEDIA_INTF_T_V4L_BASE + 5) +#define MEDIA_INTF_T_V4L_AUDIO (MEDIA_INTF_T_V4L_BASE + 6) #define MEDIA_INTF_T_ALSA_BASE 0x00000300 #define MEDIA_INTF_T_ALSA_PCM_CAPTURE (MEDIA_INTF_T_ALSA_BASE) -- 2.34.1
Add V4L2_CID_M2M_AUDIO_SOURCE_RATE and V4L2_CID_M2M_AUDIO_DEST_RATE new IDs for rate control. Add V4L2_CID_M2M_AUDIO_SOURCE_RATE_OFFSET and V4L2_CID_M2M_AUDIO_DEST_RATE_OFFSET for clock drift. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- .../media/v4l/ext-ctrls-audio-m2m.rst | 38 +++++++++++++++++++ drivers/media/v4l2-core/v4l2-ctrls-defs.c | 6 +++ include/uapi/linux/v4l2-controls.h | 5 +++ 3 files changed, 49 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst index 82d2ecedbfee..b137b7c442e6 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst @@ -19,3 +19,41 @@ Audio M2M Control IDs The Audio M2M class descriptor. Calling :ref:`VIDIOC_QUERYCTRL` for this control will return a description of this control class. + +.. _v4l2-audio-asrc: + +``V4L2_CID_M2M_AUDIO_SOURCE_RATE (integer menu)`` + This control specifies the audio source sample rate, unit is Hz + +``V4L2_CID_M2M_AUDIO_DEST_RATE (integer menu)`` + This control specifies the audio destination sample rate, unit is Hz + +``V4L2_CID_M2M_AUDIO_SOURCE_RATE_OFFSET (fixed point)`` + This control specifies the offset from the audio source sample rate, + unit is Hz. + + The offset compensates for any clock drift. The actual source audio + sample rate is the ideal source audio sample rate from + ``V4L2_CID_M2M_AUDIO_SOURCE_RATE`` plus this fixed point offset. + + The audio source clock may have some drift. Reducing or increasing the + audio sample rate dynamically to ensure that Sample Rate Converter is + working on the real sample rate, this feature is for the Asynchronous + Sample Rate Converter module. + So, userspace would be expected to be monitoring such drift + and increasing/decreasing the sample frequency as needed by this control. + +``V4L2_CID_M2M_AUDIO_DEST_RATE_OFFSET (fixed point)`` + This control specifies the offset from the audio destination sample rate, + unit is Hz. + + The offset compensates for any clock drift. The actual destination audio + sample rate is the ideal source audio sample rate from + ``V4L2_CID_M2M_AUDIO_DEST_RATE`` plus this fixed point offset. + + The audio destination clock may have some drift. Reducing or increasing + the audio sample rate dynamically to ensure that sample rate converter + is working on the real sample rate, this feature is for the Asynchronous + Sample Rate Converter module. + So, userspace would be expected to be monitoring such drift + and increasing/decreasing the sample frequency as needed by this control. diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 2a85ea3dc92f..91e1f5348c23 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -1245,6 +1245,8 @@ const char *v4l2_ctrl_get_name(u32 id) /* Audio M2M controls */ case V4L2_CID_M2M_AUDIO_CLASS: return "Audio M2M Controls"; + case V4L2_CID_M2M_AUDIO_SOURCE_RATE: return "Audio Source Sample Rate"; + case V4L2_CID_M2M_AUDIO_DEST_RATE: return "Audio Destination Sample Rate"; default: return NULL; } @@ -1606,6 +1608,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY: *type = V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY; break; + case V4L2_CID_M2M_AUDIO_SOURCE_RATE: + case V4L2_CID_M2M_AUDIO_DEST_RATE: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index a8b4b830c757..30129ccdc282 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -3495,6 +3495,11 @@ struct v4l2_ctrl_av1_film_grain { #define V4L2_CID_M2M_AUDIO_CLASS_BASE (V4L2_CTRL_CLASS_M2M_AUDIO | 0x900) #define V4L2_CID_M2M_AUDIO_CLASS (V4L2_CTRL_CLASS_M2M_AUDIO | 1) +#define V4L2_CID_M2M_AUDIO_SOURCE_RATE (V4L2_CID_M2M_AUDIO_CLASS_BASE + 0) +#define V4L2_CID_M2M_AUDIO_DEST_RATE (V4L2_CID_M2M_AUDIO_CLASS_BASE + 1) +#define V4L2_CID_M2M_AUDIO_SOURCE_RATE_OFFSET (V4L2_CID_M2M_AUDIO_CLASS_BASE + 2) +#define V4L2_CID_M2M_AUDIO_DEST_RATE_OFFSET (V4L2_CID_M2M_AUDIO_CLASS_BASE + 3) + /* MPEG-compression definitions kept for backwards compatibility */ #ifndef __KERNEL__ #define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC -- 2.34.1
The Audio M2M class includes controls for audio memory-to-memory use cases. The controls can be used for audio codecs, audio preprocessing, audio postprocessing. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- .../userspace-api/media/v4l/common.rst | 1 + .../media/v4l/ext-ctrls-audio-m2m.rst | 21 +++++++++++++++++++ .../media/v4l/vidioc-g-ext-ctrls.rst | 4 ++++ drivers/media/v4l2-core/v4l2-ctrls-defs.c | 4 ++++ include/uapi/linux/v4l2-controls.h | 4 ++++ 5 files changed, 34 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst diff --git a/Documentation/userspace-api/media/v4l/common.rst b/Documentation/userspace-api/media/v4l/common.rst index ea0435182e44..d5366e96a596 100644 --- a/Documentation/userspace-api/media/v4l/common.rst +++ b/Documentation/userspace-api/media/v4l/common.rst @@ -52,6 +52,7 @@ applicable to all devices. ext-ctrls-fm-rx ext-ctrls-detect ext-ctrls-colorimetry + ext-ctrls-audio-m2m fourcc format planar-apis diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst new file mode 100644 index 000000000000..82d2ecedbfee --- /dev/null +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-audio-m2m.rst @@ -0,0 +1,21 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _audiom2m-controls: + +*************************** +Audio M2M Control Reference +*************************** + +The Audio M2M class includes controls for audio memory-to-memory +use cases. The controls can be used for audio codecs, audio +preprocessing, audio postprocessing. + +Audio M2M Control IDs +----------------------- + +.. _audiom2m-control-id: + +``V4L2_CID_M2M_AUDIO_CLASS (class)`` + The Audio M2M class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst index 4d56c0528ad7..aeb1ad8e7d29 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst @@ -488,6 +488,10 @@ still cause this situation. - 0xa50000 - The class containing colorimetry controls. These controls are described in :ref:`colorimetry-controls`. + * - ``V4L2_CTRL_CLASS_M2M_AUDIO`` + - 0xa60000 + - The class containing audio m2m controls. These controls are + described in :ref:`audiom2m-controls`. Return Value ============ diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 8696eb1cdd61..2a85ea3dc92f 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -1242,6 +1242,9 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_COLORIMETRY_CLASS: return "Colorimetry Controls"; case V4L2_CID_COLORIMETRY_HDR10_CLL_INFO: return "HDR10 Content Light Info"; case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY: return "HDR10 Mastering Display"; + + /* Audio M2M controls */ + case V4L2_CID_M2M_AUDIO_CLASS: return "Audio M2M Controls"; default: return NULL; } @@ -1451,6 +1454,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DETECT_CLASS: case V4L2_CID_CODEC_STATELESS_CLASS: case V4L2_CID_COLORIMETRY_CLASS: + case V4L2_CID_M2M_AUDIO_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read nor write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 99c3f5e99da7..a8b4b830c757 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -30,6 +30,7 @@ #define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */ #define V4L2_CTRL_CLASS_CODEC_STATELESS 0x00a40000 /* Stateless codecs controls */ #define V4L2_CTRL_CLASS_COLORIMETRY 0x00a50000 /* Colorimetry controls */ +#define V4L2_CTRL_CLASS_M2M_AUDIO 0x00a60000 /* Audio M2M controls */ /* User-class control IDs */ @@ -3491,6 +3492,9 @@ struct v4l2_ctrl_av1_film_grain { __u8 reserved[4]; }; +#define V4L2_CID_M2M_AUDIO_CLASS_BASE (V4L2_CTRL_CLASS_M2M_AUDIO | 0x900) +#define V4L2_CID_M2M_AUDIO_CLASS (V4L2_CTRL_CLASS_M2M_AUDIO | 1) + /* MPEG-compression definitions kept for backwards compatibility */ #ifndef __KERNEL__ #define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC -- 2.34.1