Exynos5 Series SCALER Driver
Subsystems (3)
v6 cover Exynos5 Series SCALER Driver
This patch adds support for SCALER device which is a new device for scaling, blending, color fill and color space conversion on EXYNOS5410/5420 SoCs.
This device supports the following as key features.
input image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888, L8A8 and L8
output image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888
input rotation
- 0/90/180/270 degree, X/Y/XY Flip
scale ratio
- 1/4 scale down to 16 scale up
color space conversion
- RGB to YUV / YUV to RGB
Size - Exynos5420
- Input : 16x16 to 8192x8192
- Output: 4x4 to 8192x8192
Size - Exynos5410
- Input/Output: 4x4 to 4096x4096
alpha blending, color fill
Rebased on:
-----------git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git:master
Changes from v5:
---------------
Addressed review comments from, Bartlomiej Zolnierkiewicz and Tomasz Figa Links to the review comments:
1] https://linuxtv.org/patch/21512/ 2] https://linuxtv.org/patch/21513/ 3] https://linuxtv.org/patch/21514/
Changes from v4:
---------------
Addressed review comments from, Sylwester Nawrocki and Mauro Carvalho Chehab Links to the review comments:
1] https://linuxtv.org/patch/20307/ 2] https://linuxtv.org/patch/20308/ 3] https://linuxtv.org/patch/20451/
Changes from v3:
---------------
Addressed review comments from, Sylwester Nawrocki and Hans Verkuil. Links to the review comments:
1] https://linuxtv.org/patch/20072/ 2] https://linuxtv.org/patch/20073/
Changes from v2:
---------------
Addressed review comments from, Inki Dae, Hans Verkuil and Sylwester Nawrocki. Links to the review comments:
1] https://linuxtv.org/patch/19783/ 2] https://linuxtv.org/patch/19784/ 3] https://linuxtv.org/patch/19785/ 4] https://linuxtv.org/patch/19786/ 5] https://linuxtv.org/patch/19787/
Changes from v1:
---------------
1] Split the previous single patch into multiple patches.
2] Added DT binding documentation.
3] Removed the unnecessary header file inclusions.
4] Fix the condition check in mscl_prepare_address for swapping cb/cr addresses.
Shaik Ameer Basha (4):
[media] exynos-scaler: Add DT bindings for SCALER driver [media] exynos-scaler: Add new driver for Exynos5 SCALER [media] exynos-scaler: Add m2m functionality for the SCALER driver [media] exynos-scaler: Add core functionality for the SCALER driver
.../devicetree/bindings/media/exynos5-scaler.txt | 24 + drivers/media/platform/Kconfig | 8 + drivers/media/platform/Makefile | 1 + drivers/media/platform/exynos-scaler/Makefile | 3 + drivers/media/platform/exynos-scaler/scaler-m2m.c | 788 +++++++++++++ drivers/media/platform/exynos-scaler/scaler-regs.c | 337 ++++++ drivers/media/platform/exynos-scaler/scaler-regs.h | 331 ++++++ drivers/media/platform/exynos-scaler/scaler.c | 1235 ++++++++++++++++++++ drivers/media/platform/exynos-scaler/scaler.h | 376 ++++++ 9 files changed, 3103 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt create mode 100644 drivers/media/platform/exynos-scaler/Makefile create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.c create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.c create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.h create mode 100644 drivers/media/platform/exynos-scaler/scaler.c create mode 100644 drivers/media/platform/exynos-scaler/scaler.h
Signature · 1 line
1.7.9.5
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.
--- .../devicetree/bindings/media/exynos5-scaler.txt | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +24 -0
@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. + +Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler" +- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier +- clocks: should contain the SCALER clock phandle and specifier pair for + each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clock + +Example: + scaler_0: scaler at 12800000 { + compatible = "samsung,exynos5420-scaler"; + reg = <0x12800000 0x1000>; + interrupts = <0 220 0>; + clocks = <&clock 381>; + clock-names = "scaler"; + };
Signature · 1 line
1.7.9.5
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
Hi Shaik,
Thank you for the patch.
Quoted 23 lines · On Wednesday 19 March 2014 12:43:13 Shaik Ameer Basha wrote:
This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.
Signed-off-by Shaik Ameer Basha <[email protected]>Reviewed-by Sylwester Nawrocki <[email protected]>--- .../devicetree/bindings/media/exynos5-scaler.txt | 24 +++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644
Documentation/devicetree/bindings/media/exynos5-scaler.txt(quoted diff) +0
-0
b/Documentation/devicetree/bindings/media/exynos5-scaler.txtnew file mode 100644
index 0000000..e1dd465Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +5
-0@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. +
Documentation/devicetree/bindings/media/exynos5-scaler.txt line 15
Quoted 10 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10
-0+Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler" +- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier +- clocks: should contain the SCALER clock phandle and specifier pair for + each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clock
I'm not too familiar with the Exynos platform, but wouldn't it make sense to use a common name across IP cores for interface and function clocks ?
Quoted 8 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +8
-0+Example: + scaler_0: scaler@12800000 { + compatible = "samsung,exynos5420-scaler"; + reg = <0x12800000 0x1000>; + interrupts = <0 220 0>; + clocks = <&clock 381>; + clock-names = "scaler"; + };
Signature · 3 lines
Regards, Laurent Pinchart
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
Hi,
Quoted 12 lines · Laurent Pinchart
Quoted 12 lines
+++
b/Documentation/devicetree/bindings/media/exynos5-scaler.txtQuoted 11 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10
-0@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. + +Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler" +- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier
Quoted 8 lines
Quoted 5 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt line 15
Quoted 5 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +5
-0+- clocks: should contain the SCALER clock phandle and specifier pair for + each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clockI'm not too familiar with the Exynos platform, but wouldn't it make sense to use a common name across IP cores for interface and function clocks ?
Certainly it would, I proposed that when the exynos clock controller driver was posted, quite long time ago. Unfortunately it wasn't followed up. One of serious reasons was that there are common drivers for multiple Samsung platforms (also the ones not reworked to support dt) and thus changing the clock names would be problematic - driver would still need to handle multiple clock names.
But for this driver a common name like "gate" could be better IMHO.
Regards,
Sylwester
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
Hi Sylwester,
Quoted 23 lines · On Wednesday 19 March 2014 12:55:21 Sylwester Nawrocki wrote:
Quoted 14 lines · Laurent Pinchart
Quoted 14 lines
+++
b/Documentation/devicetree/bindings/media/exynos5-scaler.txtQuoted 12 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +11
-0@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. + +Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler" +- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier +- clocks: should contain the SCALER clock phandle and specifier pairQuoted 8 lines
Quoted 5 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt line 15
Quoted 5 lines
for
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +4
-0+ each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clockI'm not too familiar with the Exynos platform, but wouldn't it make sense to use a common name across IP cores for interface and function clocks ?
Quoted 7 lines
Certainly it would, I proposed that when the exynos clock controller driver was posted, quite long time ago. Unfortunately it wasn't followed up. One of serious reasons was that there are common drivers for multiple Samsung platforms (also the ones not reworked to support dt) and thus changing the clock names would be problematic - driver would still need to handle multiple clock names.
But for this driver a common name like "gate" could be better IMHO.
OMAP uses "ick" for the interface clock and "fck" for the function clock. Do you think it would make sense to standardize on "fck" across SoC families ?
Signature · 3 lines
Regards, Laurent Pinchart
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
Hi Laurent,
Quoted 32 lines · Laurent Pinchart
Quoted 23 lines · On Wednesday 19 March 2014 12:55:21 Sylwester Nawrocki wrote:
Quoted 23 lines · Laurent Pinchart
Quoted 22 lines
Quoted 22 lines
Quoted 11 lines
Quoted 11 lines
+++
b/Documentation/devicetree/bindings/media/exynos5-scaler.txtQuoted 9 lines
Quoted 9 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +8
-0@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. + +Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler"Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt line 15
Quoted 8 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +3
-0+- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier +- clocks: should contain the SCALER clock phandle and specifier pairfor
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +4
-0+ each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clockI'm not too familiar with the Exynos platform, but wouldn't it make sense to use a common name across IP cores for interface and function clocks ?
Quoted 7 lines
Quoted 7 lines
Certainly it would, I proposed that when the exynos clock controller driver was posted, quite long time ago. Unfortunately it wasn't followed up. One of serious reasons was that there are common drivers for multiple Samsung platforms (also the ones not reworked to support dt) and thus changing the clock names would be problematic - driver would still need to handle multiple clock names.
But for this driver a common name like "gate" could be better IMHO.
Quoted 2 lines
OMAP uses "ick" for the interface clock and "fck" for the function clock. Do you think it would make sense to standardize on "fck" across SoC families ?
Not sure if it could be future proof, I guess where it isn't bound to other issues and the names could be chosen freely - we could try to use standard names. OTOH it could obfuscate things a bit. As far as Samsung platforms are concerned, the function clock names are usually in form of SCLK_[IP_NAME], "sclk" seems to be an abbreviation of "special clock".
It is also not always clear which clock is the interface clock and which is the function clock. The "special clocks" are generally referred to as the IP block clocks.
--
Regards,
Sylwester
PATCH v6 1/4 exynos-scaler: Add DT bindings for SCALER driver
In reply to Laurent PinchartHi Laurent,
Quoted 33 lines · Laurent Pinchart
Hi Shaik,
Thank you for the patch.
Quoted 28 lines · On Wednesday 19 March 2014 12:43:13 Shaik Ameer Basha wrote:
This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.
Signed-off-by Shaik Ameer Basha <[email protected]>Reviewed-by Sylwester Nawrocki <[email protected]>--- .../devicetree/bindings/media/exynos5-scaler.txt | 24 +++++++++++++++++ 1 file changed, 24 insertions(+)create mode 100644
Documentation/devicetree/bindings/media/exynos5-scaler.txt(quoted diff) +0
-0
b/Documentation/devicetree/bindings/media/exynos5-scaler.txtnew file mode 100644
index 0000000..e1dd465Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10
-0@@ -0,0 +1,24 @@ +* Samsung Exynos5 SCALER device + +SCALER is used for scaling, blending, color fill and color space +conversion on EXYNOS[5420/5410] SoCs. + +Required properties: +- compatible: should be "samsung,exynos5420-scaler" or + "samsung,exynos5410-scaler" +- reg: should contain SCALER physical address location and length +- interrupts: should contain SCALER interrupt specifier
Quoted 8 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt line 15
Quoted 5 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +5
-0+- clocks: should contain the SCALER clock phandle and specifier pair for + each clock listed in clock-names property, according to + the common clock bindings +- clock-names: should contain exactly one entry + - "scaler" - IP bus clockI'm not too familiar with the Exynos platform, but wouldn't it make sense to use a common name across IP cores for interface and function clocks ?
Yes, it would definitely make sense, provided we are starting from scratch, but due to the lack of listed IP clock inputs in documentation, we ended with a custom of naming the inputs after SoC clocks of first SoC used with such driver. This showed up long before adoption of DT and common clocks on Samsung platform and I'd say it would be hard to get rid of it now.
Anyway, as long as clock names are well specified in binding documentation, it should be fine. So, from me it's a
Best regards,
Tomasz
PATCH v6 2/4 exynos-scaler: Add new driver for Exynos5 SCALER
In reply to Shaik Ameer BashaThis patch adds support for SCALER device which is a new device for scaling, blending, color fill and color space conversion on EXYNOS5410 and EXYNOS5420 SoCs.
This device supports the followings as key feature.
input image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888, L8A8 and L8
output image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888
input rotation
- 0/90/180/270 degree, X/Y/XY Flip
scale ratio
- 1/4 scale down to 16 scale up
color space conversion
- RGB to YUV / YUV to RGB
Size - Exynos5420
- Input : 16x16 to 8192x8192
- Output: 4x4 to 8192x8192
Size - Exynos5410
- Input/Output: 4x4 to 4096x4096
alpha blending, color fill
--- drivers/media/platform/exynos-scaler/scaler-regs.c | 337 ++++++++++++++++++++ drivers/media/platform/exynos-scaler/scaler-regs.h | 331 +++++++++++++++++++ 2 files changed, 668 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.c create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.h
drivers/media/platform/exynos-scaler/scaler-regs.c new file +337 -0
@@ -0,0 +1,337 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* http://www.samsung.com +* +* Samsung EXYNOS5 SoC series SCALER driver +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include "scaler-regs.h" + +/* Scaler reset timeout in milliseconds */ +#define SCALER_RESET_TIMEOUT 5 + +void scaler_hw_set_sw_reset (struct scaler_dev *dev) +{ +u32 cfg; + + cfg =scaler_read (dev,SCALER_CFG ); + cfg |=SCALER_CFG_SOFT_RESET ; + +scaler_write (dev,SCALER_CFG , cfg); +} + +int scaler_wait_reset (struct scaler_dev *dev) +{ +unsigned long end = jiffies +msecs_to_jiffies (SCALER_RESET_TIMEOUT ); +u32 cfg, reset_done =0 ; + +while (time_is_after_jiffies (end)) { + cfg =scaler_read (dev,SCALER_CFG ); +if (!(cfg &SCALER_CFG_SOFT_RESET )) { + reset_done =1 ; +break ; + } +usleep_range (100 ,200 ); + } + +if (!reset_done) +return -EBUSY ; + +/* +* Write any value to read/write register and read it back. +* If the write and read value matches, then the reset process is +* succeeded. +*/ + end = jiffies +msecs_to_jiffies (SCALER_RESET_TIMEOUT ); +while (time_is_after_jiffies (end)) { +scaler_write (dev,SCALER_CFG_SOFT_RESET_CHECK_REG , +SCALER_CFG_SOFT_RESET_CHECK_VAL ); +if (SCALER_CFG_SOFT_RESET_CHECK_VAL == +scaler_read (dev,SCALER_CFG_SOFT_RESET_CHECK_REG )) +return 0 ; +usleep_range (9000 ,10000 ); + } + +return -EBUSY ; +} + +void scaler_hw_set_irq (struct scaler_dev *dev,int irq_num,bool enable) +{ +u32 cfg; + +if ((irq_num <SCALER_INT_FRAME_END ) || + (irq_num >SCALER_INT_TIMEOUT )) +return ; + + cfg =scaler_read (dev,SCALER_INT_EN ); +if (enable) + cfg |=1 << irq_num; +else + cfg &= ~(1 << irq_num); +scaler_write (dev,SCALER_INT_EN , cfg); +} + +void scaler_hw_set_input_addr (struct scaler_dev *dev,struct scaler_addr *addr) +{ +scaler_dbg (dev,"src_buf: 0x%x, cb: 0x%x, cr: 0x%x" , + addr->y, addr->cb, addr->cr); +scaler_write (dev,SCALER_SRC_Y_BASE , addr->y); +scaler_write (dev,SCALER_SRC_CB_BASE , addr->cb); +scaler_write (dev,SCALER_SRC_CR_BASE , addr->cr); +} + +void scaler_hw_set_output_addr (struct scaler_dev *dev, +struct scaler_addr *addr) +{ +scaler_dbg (dev,"dst_buf: 0x%x, cb: 0x%x, cr: 0x%x" , + addr->y, addr->cb, addr->cr); +scaler_write (dev,SCALER_DST_Y_BASE , addr->y); +scaler_write (dev,SCALER_DST_CB_BASE , addr->cb); +scaler_write (dev,SCALER_DST_CR_BASE , addr->cr); +} + +void scaler_hw_set_in_size (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +struct scaler_frame *frame = &ctx->s_frame; +u32 cfg; + +/* set input pixel offset */ + cfg = (frame->selection.left &SCALER_SRC_YH_POS_MASK ) << +SCALER_SRC_YH_POS_SHIFT ; + cfg |= (frame->selection.top &SCALER_SRC_YV_POS_MASK ) << +SCALER_SRC_YV_POS_SHIFT ; +scaler_write (dev,SCALER_SRC_Y_POS , cfg); + +/* TODO: calculate 'C' plane h/v offset using 'Y' plane h/v offset */ + +/* Set input span */ + cfg = (frame->f_width &SCALER_SRC_Y_SPAN_MASK ) << +SCALER_SRC_Y_SPAN_SHIFT ; +if (is_yuv420_2p (frame->fmt)) + cfg |= (frame->f_width &SCALER_SRC_C_SPAN_MASK ) << +SCALER_SRC_C_SPAN_SHIFT ; +else /* TODO: Verify */ + cfg |= (frame->f_width &SCALER_SRC_C_SPAN_MASK ) << +SCALER_SRC_C_SPAN_SHIFT ; + +scaler_write (dev,SCALER_SRC_SPAN , cfg); + +/* Set input cropped size */ + cfg = (frame->selection.width &SCALER_SRC_WIDTH_MASK ) << +SCALER_SRC_WIDTH_SHIFT ; + cfg |= (frame->selection.height &SCALER_SRC_HEIGHT_MASK ) << +SCALER_SRC_HEIGHT_SHIFT ; +scaler_write (dev,SCALER_SRC_WH , cfg); + +scaler_dbg (dev,"src: posx: %d, posY: %d, spanY: %d, spanC: %d\n" , + frame->selection.left, frame->selection.top, + frame->f_width, frame->f_width); +scaler_dbg (dev,"cropX: %d, cropY: %d\n" , + frame->selection.width, frame->selection.height); +} + +void scaler_hw_set_in_image_format (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +struct scaler_frame *frame = &ctx->s_frame; +u32 cfg; + + cfg =scaler_read (dev,SCALER_SRC_CFG ); + cfg &= ~(SCALER_SRC_COLOR_FORMAT_MASK <<SCALER_SRC_COLOR_FORMAT_SHIFT ); + cfg |= (frame->fmt->scaler_color &SCALER_SRC_COLOR_FORMAT_MASK ) << +SCALER_SRC_COLOR_FORMAT_SHIFT ; + +/* Setting tiled/linear format */ +if (is_tiled_fmt (frame->fmt)) + cfg |=SCALER_SRC_TILE_EN ; +else + cfg &= ~SCALER_SRC_TILE_EN ; + +scaler_write (dev,SCALER_SRC_CFG , cfg); +} + +void scaler_hw_set_out_size (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +struct scaler_frame *frame = &ctx->d_frame; +u32 cfg; + +/* Set output pixel offset */ + cfg = (frame->selection.left &SCALER_DST_H_POS_MASK ) << +SCALER_DST_H_POS_SHIFT ; + cfg |= (frame->selection.top &SCALER_DST_V_POS_MASK ) << +SCALER_DST_V_POS_SHIFT ; +scaler_write (dev,SCALER_DST_POS , cfg); + +/* Set output span */ + cfg = (frame->f_width &SCALER_DST_Y_SPAN_MASK ) << +SCALER_DST_Y_SPAN_SHIFT ; +if (is_yuv420_2p (frame->fmt)) + cfg |= ((frame->f_width /2 ) &SCALER_DST_C_SPAN_MASK ) << +SCALER_DST_C_SPAN_SHIFT ; +else + cfg |= (frame->f_width &SCALER_DST_C_SPAN_MASK ) << +SCALER_DST_C_SPAN_SHIFT ; +scaler_write (dev,SCALER_DST_SPAN , cfg); + +/* Set output scaled size */ + cfg = (frame->selection.width &SCALER_DST_WIDTH_MASK ) << +SCALER_DST_WIDTH_SHIFT ; + cfg |= (frame->selection.height &SCALER_DST_HEIGHT_MASK ) << +SCALER_DST_HEIGHT_SHIFT ; +scaler_write (dev,SCALER_DST_WH , cfg); + +scaler_dbg (dev,"dst: pos X: %d, pos Y: %d, span Y: %d, span C: %d\n" , + frame->selection.left, frame->selection.top, + frame->f_width, frame->f_width); +scaler_dbg (dev,"crop X: %d, crop Y: %d\n" , + frame->selection.width, frame->selection.height); +} + +void scaler_hw_set_out_image_format (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +struct scaler_frame *frame = &ctx->d_frame; +u32 cfg; + + cfg =scaler_read (dev,SCALER_DST_CFG ); + cfg &= ~SCALER_DST_COLOR_FORMAT_MASK ; + cfg |= (frame->fmt->scaler_color &SCALER_DST_COLOR_FORMAT_MASK ); + +scaler_write (dev,SCALER_DST_CFG , cfg); +} + +void scaler_hw_set_scaler_ratio (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +struct scaler_scaler *sc = &ctx->scaler; +u32 cfg; + + cfg = (sc->hratio &SCALER_H_RATIO_MASK ) <<SCALER_H_RATIO_SHIFT ; +scaler_write (dev,SCALER_H_RATIO , cfg); + + cfg = (sc->vratio &SCALER_V_RATIO_MASK ) <<SCALER_V_RATIO_SHIFT ; +scaler_write (dev,SCALER_V_RATIO , cfg); +} + +void scaler_hw_set_rotation (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +u32 cfg =0 ; + + cfg = ((ctx->ctrls_scaler.rotate->val /90 ) &SCALER_ROTMODE_MASK ) << +SCALER_ROTMODE_SHIFT ; + +if (ctx->ctrls_scaler.hflip->val) + cfg |=SCALER_FLIP_X_EN ; + +if (ctx->ctrls_scaler.vflip->val) + cfg |=SCALER_FLIP_Y_EN ; + +scaler_write (dev,SCALER_ROT_CFG , cfg); +} + +void scaler_hw_set_csc_coeff (struct scaler_ctx *ctx) +{ +struct scaler_dev *dev = ctx->scaler_dev; +enum scaler_csc_coeff type; +u32 cfg =0 ; +int i, j; +static const u32 csc_coeff[SCALER_CSC_COEFF_MAX ][3 ][3 ] = { + {/* YCbCr to RGB */ + {0x254 ,0x000 ,0x331 }, + {0x254 ,0xec8 ,0xFA0 }, + {0x254 ,0x409 ,0x000 } + }, + {/* RGB to YCbCr */ + {0x084 ,0x102 ,0x032 }, + {0xe4c ,0xe95 ,0x0e1 }, + {0x0e1 ,0xebc ,0xe24 } + } }; + +/* TODO: add check for BT.601,BT.709 narrow/wide ranges */ +if (is_rgb (ctx->s_frame.fmt) ==is_rgb (ctx->d_frame.fmt)) { + type =SCALER_CSC_COEFF_NONE ; + }else if (is_rgb (ctx->d_frame.fmt)) { + type =SCALER_CSC_COEFF_YCBCR_TO_RGB ; +scaler_hw_src_y_offset_en (ctx->scaler_dev, true); + }else { + type =SCALER_CSC_COEFF_RGB_TO_YCBCR ; +scaler_hw_src_y_offset_en (ctx->scaler_dev, true); + } + +if (type == ctx->scaler_dev->coeff_type || type >=SCALER_CSC_COEFF_MAX ) +return ; + +for (i =0 ; i <3 ; i++) { +for (j =0 ; j <3 ; j++) { + cfg = csc_coeff[type][i][j]; +scaler_write (dev,SCALER_CSC_COEF (i, j), cfg); + } + } + + ctx->scaler_dev->coeff_type = type; +} + +void scaler_hw_src_y_offset_en (struct scaler_dev *dev,bool on) +{ +u32 cfg; + + cfg =scaler_read (dev,SCALER_CFG ); +if (on) + cfg |=SCALER_CFG_CSC_Y_OFFSET_SRC_EN ; +else + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_SRC_EN ; + +scaler_write (dev,SCALER_CFG , cfg); +} + +void scaler_hw_dst_y_offset_en (struct scaler_dev *dev,bool on) +{ +u32 cfg; + + cfg =scaler_read (dev,SCALER_CFG ); +if (on) + cfg |=SCALER_CFG_CSC_Y_OFFSET_DST_EN ; +else + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_DST_EN ; + +scaler_write (dev,SCALER_CFG , cfg); +} + +void scaler_hw_enable_control (struct scaler_dev *dev,bool on) +{ +u32 cfg; + +if (on) +scaler_write (dev,SCALER_INT_EN ,0xffffffff ); + + cfg =scaler_read (dev,SCALER_CFG ); + cfg |=SCALER_CFG_16_BURST_MODE ; +if (on) + cfg |=SCALER_CFG_START_CMD ; +else + cfg &= ~SCALER_CFG_START_CMD ; + +scaler_dbg (dev,"%s: SCALER_CFG:0x%x\n" , __func__, cfg); + +scaler_write (dev,SCALER_CFG , cfg); +} + +unsigned int scaler_hw_get_irq_status (struct scaler_dev *dev) +{ +return scaler_read (dev,SCALER_INT_STATUS ); +} + +void scaler_hw_clear_irq (struct scaler_dev *dev,unsigned int irq) +{ +scaler_write (dev,SCALER_INT_STATUS , irq); +}
drivers/media/platform/exynos-scaler/scaler-regs.h new file +331 -0
331 lines not on this page · raw message
Signature · 1 line
1.7.9.5
PATCH v6 3/4 exynos-scaler: Add m2m functionality for the SCALER driver
In reply to Shaik Ameer BashaThis patch adds memory to memory (m2m) interface functionality for the SCALER driver.
[arun.kk at samsung.com: fix compilation issues]
--- drivers/media/platform/exynos-scaler/scaler-m2m.c | 788 +++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.c
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +788 -0
@@ -0,0 +1,788 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* http://www.samsung.com +* +* Samsung EXYNOS5 SoC series SCALER driver +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <media/v4l2-ioctl.h> + +#include "scaler-regs.h" + +#define SCALER_DEF_PIX_FMT V4L2_PIX_FMT_RGB32 +#define SCALER_DEF_WIDTH 1280 +#define SCALER_DEF_HEIGHT 720 + +static int scaler_m2m_ctx_stop_req (struct scaler_ctx *ctx) +{ +struct scaler_ctx *curr_ctx; +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + + curr_ctx =v4l2_m2m_get_curr_priv (scaler->m2m.m2m_dev); +if (!scaler_m2m_pending (scaler) || (curr_ctx != ctx)) +return 0 ; + +scaler_ctx_state_lock_set (SCALER_CTX_STOP_REQ , ctx); + ret =wait_event_timeout (scaler->irq_queue, + !scaler_ctx_state_is_set (SCALER_CTX_STOP_REQ , ctx), +SCALER_SHUTDOWN_TIMEOUT ); + +return ret ==0 ? -ETIMEDOUT : ret; +} + +static int scaler_m2m_start_streaming (struct vb2_queue *q,unsigned int count) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =pm_runtime_get_sync (&ctx->scaler_dev->pdev->dev); + +return ret >0 ?0 : ret; +} + +static int scaler_m2m_stop_streaming (struct vb2_queue *q) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); + +pm_runtime_put (&ctx->scaler_dev->pdev->dev); + +return 0 ; +} + +void scaler_m2m_job_finish (struct scaler_ctx *ctx,int vb_state) +{ +struct vb2_buffer *src_vb, *dst_vb; + +if (!ctx || !ctx->m2m_ctx) +return ; + + src_vb =v4l2_m2m_src_buf_remove (ctx->m2m_ctx); + dst_vb =v4l2_m2m_dst_buf_remove (ctx->m2m_ctx); + +if (src_vb && dst_vb) { +v4l2_m2m_buf_done (src_vb, vb_state); +v4l2_m2m_buf_done (dst_vb, vb_state); + +v4l2_m2m_job_finish (ctx->scaler_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +static void scaler_m2m_job_abort (void *priv) +{ +struct scaler_ctx *ctx = priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); +} + +static int scaler_get_bufs (struct scaler_ctx *ctx) +{ +struct scaler_frame *s_frame, *d_frame; +struct vb2_buffer *src_vb, *dst_vb; +int ret; + + s_frame = &ctx->s_frame; + d_frame = &ctx->d_frame; + + src_vb =v4l2_m2m_next_src_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, src_vb, s_frame, &s_frame->addr); +if (ret <0 ) +return ret; + + dst_vb =v4l2_m2m_next_dst_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, dst_vb, d_frame, &d_frame->addr); +if (ret <0 ) +return ret; + + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + +return 0 ; +} + +static void scaler_m2m_device_run (void *priv) +{ +struct scaler_ctx *ctx = priv; +struct scaler_dev *scaler; +unsigned long flags; +int ret; +bool is_stopped; + +if (WARN (!ctx,"Null hardware context\n" )) +return ; + + scaler = ctx->scaler_dev; +spin_lock_irqsave (&scaler->slock, flags); + +set_bit (ST_M2M_PEND , &scaler->state); + +/* Reconfigure hardware if the context has changed. */ +if (scaler->m2m.ctx != ctx) { +scaler_dbg (scaler,"scaler->m2m.ctx = 0x%p, current_ctx = 0x%p" , + scaler->m2m.ctx, ctx); + ctx->state |=SCALER_PARAMS ; + scaler->m2m.ctx = ctx; + } + + is_stopped = ctx->state &SCALER_CTX_STOP_REQ ; + ctx->state &= ~SCALER_CTX_STOP_REQ ; +if (is_stopped) { +wake_up (&scaler->irq_queue); + goto unlock; + } + + ret =scaler_get_bufs (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Wrong address\n" ); + goto unlock; + } + +scaler_hw_address_queue_reset (ctx); +scaler_set_prefbuf (scaler, &ctx->s_frame); +scaler_hw_set_input_addr (scaler, &ctx->s_frame.addr); +scaler_hw_set_output_addr (scaler, &ctx->d_frame.addr); +scaler_hw_set_csc_coeff (ctx); + +if (ctx->state &SCALER_PARAMS ) { +scaler_hw_set_irq (scaler,SCALER_INT_FRAME_END , false); +if (scaler_set_scaler_info (ctx)) { +scaler_dbg (scaler,"Scaler setup error" ); + goto unlock; + } + +scaler_hw_set_in_size (ctx); +scaler_hw_set_in_image_format (ctx); + +scaler_hw_set_out_size (ctx); +scaler_hw_set_out_image_format (ctx); + +scaler_hw_set_scaler_ratio (ctx); +scaler_hw_set_rotation (ctx); + } + + ctx->state &= ~SCALER_PARAMS ; +scaler_hw_enable_control (scaler, true); + +spin_unlock_irqrestore (&scaler->slock, flags); +return ; + +unlock: + ctx->state &= ~SCALER_PARAMS ; +spin_unlock_irqrestore (&scaler->slock, flags); +} + +static int scaler_m2m_queue_setup (struct vb2_queue *vq, +const struct v4l2_format *fmt, +unsigned int *num_buffers,unsigned int *num_planes, +unsigned int sizes[],void *allocators[]) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vq->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (!frame->fmt) +return -EINVAL ; + + *num_planes = frame->fmt->num_planes; +for (i =0 ; i < frame->fmt->num_planes; i++) { + sizes[i] = frame->payload[i]; + allocators[i] = ctx->scaler_dev->alloc_ctx; + } +return 0 ; +} + +static int scaler_m2m_buf_prepare (struct vb2_buffer *vb) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vb->vb2_queue->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +for (i =0 ; i < frame->fmt->num_planes; i++) +vb2_set_plane_payload (vb, i, frame->payload[i]); + +return 0 ; +} + +static void scaler_m2m_buf_queue (struct vb2_buffer *vb) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); + +scaler_dbg (ctx->scaler_dev,"ctx: %p, ctx->state: 0x%x" , + ctx, ctx->state); + +if (ctx->m2m_ctx) +v4l2_m2m_buf_queue (ctx->m2m_ctx, vb); +} + +static void scaler_lock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_lock (&ctx->scaler_dev->lock); +} + +static void scaler_unlock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_unlock (&ctx->scaler_dev->lock); +} + +static struct vb2_ops scaler_m2m_qops = { + .queue_setup = scaler_m2m_queue_setup, + .buf_prepare = scaler_m2m_buf_prepare, + .buf_queue = scaler_m2m_buf_queue, + .wait_prepare = scaler_unlock, + .wait_finish = scaler_lock, + .stop_streaming = scaler_m2m_stop_streaming, + .start_streaming = scaler_m2m_start_streaming, +}; + +static int scaler_m2m_querycap (struct file *file,void *fh, +struct v4l2_capability *cap) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; + + cap->device_caps =V4L2_CAP_STREAMING |V4L2_CAP_VIDEO_M2M_MPLANE ; + cap->capabilities = cap->device_caps |V4L2_CAP_DEVICE_CAPS ; + +strlcpy (cap->driver, scaler->pdev->name,sizeof (cap->driver)); +strlcpy (cap->card, scaler->pdev->name,sizeof (cap->card)); +strlcpy (cap->bus_info,"platform:exynos5-scaler" , +sizeof (cap->bus_info)); + +return 0 ; +} + +static int scaler_m2m_enum_fmt_mplane (struct file *file,void *priv, +struct v4l2_fmtdesc *f) +{ +return scaler_enum_fmt_mplane (f); +} + +static int scaler_m2m_g_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_g_fmt_mplane (ctx, f); +} + +static int scaler_m2m_try_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_try_fmt_mplane (ctx, f); +} + +static int scaler_m2m_set_default_format (struct scaler_ctx *ctx) +{ +struct scaler_frame *frame; +u32 def_pixformat =SCALER_DEF_PIX_FMT ; +int i; + + frame = &ctx->s_frame; + + frame->colorspace =V4L2_COLORSPACE_REC709 ; + frame->fmt =scaler_find_fmt (&def_pixformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +scaler_set_frame_size (frame,SCALER_DEF_WIDTH ,SCALER_DEF_HEIGHT ); +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] =SCALER_DEF_WIDTH *SCALER_DEF_HEIGHT * + (frame->fmt->depth[i] /8 ); + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +/* Apply the same src frame settings to dst frame */ + ctx->d_frame = ctx->s_frame; + +return 0 ; +} + + +static int scaler_m2m_s_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct vb2_queue *vq; +struct scaler_frame *frame; +struct v4l2_pix_format_mplane *pix; +int i, ret =0 ; + + ret =scaler_m2m_try_fmt_mplane (file, fh, f); +if (ret <0 ) +return ret; + + vq =v4l2_m2m_get_vq (ctx->m2m_ctx, f->type); + +if (vb2_is_streaming (vq)) { +scaler_dbg (ctx->scaler_dev,"queue (%d) busy" , f->type); +return -EBUSY ; + } + +if (V4L2_TYPE_IS_OUTPUT (f->type)) + frame = &ctx->s_frame; +else + frame = &ctx->d_frame; + + pix = &f->fmt.pix_mp; + frame->colorspace = pix->colorspace; + frame->fmt =scaler_find_fmt (&pix->pixelformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] = pix->plane_fmt[i].sizeimage; + +scaler_set_frame_size (frame, pix->width, pix->height); +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +scaler_dbg (ctx->scaler_dev,"f_w: %d, f_h: %d" , + frame->f_width, frame->f_height); + +return 0 ; +} + +static int scaler_m2m_reqbufs (struct file *file,void *fh, +struct v4l2_requestbuffers *reqbufs) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; +u32 max_cnt; + +if (reqbufs->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + max_cnt = scaler->variant->in_buf_cnt; +else + max_cnt = scaler->variant->out_buf_cnt; + +if (reqbufs->count > max_cnt) { +/* Adjust the count value as per driver supports */ + reqbufs->count = max_cnt; + } + +return v4l2_m2m_reqbufs (file, ctx->m2m_ctx, reqbufs); +} + +static int scaler_m2m_expbuf (struct file *file,void *fh, +struct v4l2_exportbuffer *eb) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_expbuf (file, ctx->m2m_ctx, eb); +} + +static int scaler_m2m_querybuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_querybuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_qbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_qbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_dqbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_dqbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_streamon (struct file *file,void *fh, +enum v4l2_buf_type type) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamon (file, ctx->m2m_ctx, type); +} + +static int scaler_m2m_streamoff (struct file *file,void *fh, +enum v4l2_buf_type type) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamoff (file, ctx->m2m_ctx, type); +} + +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int is_rectangle_enclosed (struct v4l2_rect *a,struct v4l2_rect *b) +{ +return !((a->left < b->left) || + (a->top < b->top) || + (a->left + a->width > b->left + b->width) || + (a->top + a->height > b->top + b->height)); +} + +static int scaler_m2m_g_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); + +if ((s->type !=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->type !=V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE )) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) { +if ((s->target ==V4L2_SEL_TGT_CROP_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_CROP_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_CROP ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + }else { +if ((s->target ==V4L2_SEL_TGT_COMPOSE_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_COMPOSE_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_COMPOSE ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + } + +return -EINVAL ; +} + +static int scaler_m2m_s_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct v4l2_crop cr; +struct scaler_variant *variant = ctx->scaler_dev->variant; +int ret; + + cr.type = s->type; + cr.c = s->r; + +if (!(((s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) && + (s->target ==V4L2_SEL_TGT_CROP )) || + ((s->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->target ==V4L2_SEL_TGT_COMPOSE )))) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); + + ret =scaler_try_crop (ctx, &cr); +if (ret <0 ) +return ret; + +if (s->flags &V4L2_SEL_FLAG_LE && + !is_rectangle_enclosed (&cr.c, &s->r)) +return -ERANGE ; + +if (s->flags &V4L2_SEL_FLAG_GE && + !is_rectangle_enclosed (&s->r, &cr.c)) +return -ERANGE ; + + s->r = cr.c; + +/* Check to see if scaling ratio is within supported range */ +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + ret =scaler_check_scaler_ratio (variant, + cr.c.width, cr.c.height, + ctx->d_frame.selection.width, + ctx->d_frame.selection.height, + ctx->ctrls_scaler.rotate->val); +else + ret =scaler_check_scaler_ratio (variant, + ctx->s_frame.selection.width, + ctx->s_frame.selection.height, + cr.c.width, cr.c.height, + ctx->ctrls_scaler.rotate->val); + +if (ret <0 ) { +scaler_dbg (ctx->scaler_dev,"Out of scaler range" ); +return -EINVAL ; + } + + frame->selection = cr.c; + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); +return 0 ; +} + +static const struct v4l2_ioctl_ops scaler_m2m_ioctl_ops = { + .vidioc_querycap = scaler_m2m_querycap, + .vidioc_enum_fmt_vid_cap_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_reqbufs = scaler_m2m_reqbufs, + .vidioc_expbuf = scaler_m2m_expbuf, + .vidioc_querybuf = scaler_m2m_querybuf, + .vidioc_qbuf = scaler_m2m_qbuf, + .vidioc_dqbuf = scaler_m2m_dqbuf, + .vidioc_streamon = scaler_m2m_streamon, + .vidioc_streamoff = scaler_m2m_streamoff, + .vidioc_g_selection = scaler_m2m_g_selection, + .vidioc_s_selection = scaler_m2m_s_selection +}; + +static int queue_init (void *priv,struct vb2_queue *src_vq, +struct vb2_queue *dst_vq) +{ +struct scaler_ctx *ctx = priv; +int ret; + +memset (src_vq,0 ,sizeof (*src_vq)); + src_vq->type =V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ; + src_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + src_vq->drv_priv = ctx; + src_vq->ops = &scaler_m2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + + ret =vb2_queue_init (src_vq); +if (ret <0 ) +return ret; + +memset (dst_vq,0 ,sizeof (*dst_vq)); + dst_vq->type =V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ; + dst_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + dst_vq->drv_priv = ctx; + dst_vq->ops = &scaler_m2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + +return vb2_queue_init (dst_vq); +} + +static int scaler_m2m_open (struct file *file) +{ +struct scaler_dev *scaler =video_drvdata (file); +struct scaler_ctx *ctx =NULL ; +int ret; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ctx =kzalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) { + ret = -ENOMEM ; + goto unlock; + } + +v4l2_fh_init (&ctx->fh, scaler->m2m.vfd); + ret =scaler_ctrls_create (ctx); +if (ret <0 ) + goto error_fh; + +/* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; +v4l2_fh_add (&ctx->fh); + + ctx->scaler_dev = scaler; +/* Default color format */ + ctx->s_frame.fmt =scaler_get_format (0 ); + ctx->d_frame.fmt =scaler_get_format (0 ); + ctx->flags =0 ; + + ctx->m2m_ctx =v4l2_m2m_ctx_init (scaler->m2m.m2m_dev, ctx, queue_init); +if (IS_ERR (ctx->m2m_ctx)) { +scaler_dbg (scaler,"Failed to initialize m2m context" ); + ret =PTR_ERR (ctx->m2m_ctx); + goto error_ctrls; + } + +/* Apply default format settings */ + ret =scaler_m2m_set_default_format (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Failed to set default format settings" ); + goto error_ctrls; + } + +if (v4l2_fh_is_singular_file (file)) +set_bit (ST_M2M_OPEN , &scaler->state); + +scaler_dbg (scaler,"scaler m2m driver is opened, ctx(0x%p)" , ctx); + +mutex_unlock (&scaler->lock); +return 0 ; + +error_ctrls: +scaler_ctrls_delete (ctx); +error_fh: +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); +kfree (ctx); +unlock: +mutex_unlock (&scaler->lock); +return ret; +} + +static int scaler_m2m_release (struct file *file) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +mutex_lock (&scaler->lock); + +if (v4l2_fh_is_singular_file (file)) +clear_bit (ST_M2M_OPEN , &scaler->state); + +v4l2_m2m_ctx_release (ctx->m2m_ctx); +scaler_ctrls_delete (ctx); +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); + +kfree (ctx); + +mutex_unlock (&scaler->lock); +return 0 ; +} + +static unsigned int scaler_m2m_poll (struct file *file, +struct poll_table_struct *wait) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_poll (file, ctx->m2m_ctx, wait); +mutex_unlock (&scaler->lock); + +return ret; +} + +static int scaler_m2m_mmap (struct file *file,struct vm_area_struct *vma) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_mmap (file, ctx->m2m_ctx, vma); +mutex_unlock (&scaler->lock); + +return ret; +} + +static const struct v4l2_file_operations scaler_m2m_fops = { + .owner =THIS_MODULE , + .open = scaler_m2m_open, + .release = scaler_m2m_release, + .poll = scaler_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = scaler_m2m_mmap, +}; + +static struct v4l2_m2m_ops scaler_m2m_ops = { + .device_run = scaler_m2m_device_run, + .job_abort = scaler_m2m_job_abort, +}; + +int scaler_register_m2m_device (struct scaler_dev *scaler) +{ +struct platform_device *pdev; +int ret; + +if (!scaler) +return -ENODEV ; + + pdev = scaler->pdev; + + scaler->vdev.fops = &scaler_m2m_fops; + scaler->vdev.ioctl_ops = &scaler_m2m_ioctl_ops; + scaler->vdev.release = video_device_release_empty; + scaler->vdev.lock = &scaler->lock; + scaler->vdev.vfl_dir =VFL_DIR_M2M ; + scaler->vdev.v4l2_dev = &scaler->v4l2_dev; +snprintf (scaler->vdev.name,sizeof (scaler->vdev.name),"%s:m2m" , +SCALER_MODULE_NAME ); + +video_set_drvdata (&scaler->vdev, scaler); + + scaler->m2m.vfd = &scaler->vdev; + scaler->m2m.m2m_dev =v4l2_m2m_init (&scaler_m2m_ops); +if (IS_ERR (scaler->m2m.m2m_dev)) { +dev_err (&pdev->dev,"failed to initialize v4l2-m2m device\n" ); +return PTR_ERR (scaler->m2m.m2m_dev); + } + + ret =video_register_device (&scaler->vdev,VFL_TYPE_GRABBER ,-1 ); +if (ret <0 ) { +dev_err (&pdev->dev, +"%s(): failed to register video device\n" , __func__); +v4l2_m2m_release (scaler->m2m.m2m_dev); +return ret; + } + +dev_info (&pdev->dev,"scaler m2m driver registered as /dev/video%d" , + scaler->vdev.num); +return 0 ; +} + +void scaler_unregister_m2m_device (struct scaler_dev *scaler) +{ +if (scaler) { +video_unregister_device (scaler->m2m.vfd); +v4l2_m2m_release (scaler->m2m.m2m_dev); + } +}
Signature · 1 line
1.7.9.5
PATCH v6 3/4 exynos-scaler: Add m2m functionality for the SCALER driver
Thanks for this patch series!
I've got some comments below...
Regards,
Hans
Quoted 59 lines · Shaik Ameer Basha
This patch adds memory to memory (m2m) interface functionality for the SCALER driver.
[arun.kk at samsung.com: fix compilation issues]
Signed-off-by Shaik Ameer Basha <[email protected]>Signed-off-by Arun Kumar K <[email protected]>Reviewed-by Sylwester Nawrocki <[email protected]>--- drivers/media/platform/exynos-scaler/scaler-m2m.c | 788 +++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.cdrivers/media/platform/exynos-scaler/scaler-m2m.c new file +40
-0@@ -0,0 +1,788 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* http://www.samsung.com +* +* Samsung EXYNOS5 SoC series SCALER driver +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <media/v4l2-ioctl.h> + +#include "scaler-regs.h" + +#define SCALER_DEF_PIX_FMT V4L2_PIX_FMT_RGB32 +#define SCALER_DEF_WIDTH 1280 +#define SCALER_DEF_HEIGHT 720 + +static int scaler_m2m_ctx_stop_req (struct scaler_ctx *ctx) +{ +struct scaler_ctx *curr_ctx; +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + + curr_ctx =v4l2_m2m_get_curr_priv (scaler->m2m.m2m_dev); +if (!scaler_m2m_pending (scaler) || (curr_ctx != ctx)) +return 0 ; + +scaler_ctx_state_lock_set (SCALER_CTX_STOP_REQ , ctx); + ret =wait_event_timeout (scaler->irq_queue, + !scaler_ctx_state_is_set (SCALER_CTX_STOP_REQ , ctx), +SCALER_SHUTDOWN_TIMEOUT ); + +return ret ==0 ? -ETIMEDOUT : ret; +}
drivers/media/platform/exynos-scaler/scaler-m2m.c line 50
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+ +static int scaler_m2m_start_streaming (struct vb2_queue *q,unsigned int count) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =pm_runtime_get_sync (&ctx->scaler_dev->pdev->dev); + +return ret >0 ?0 : ret; +}
If an error occurs, then all queued buffers must be returned to the vb2 core with vb2_buffer_done with state VB2_BUF_STATE_QUEUED. A lot of vb2 instrumentation has been added in recent weeks to check for correct buffer handling, and this was one of the corner cases that was never handled correctly. If you compile with the VIDEO_ADV_DEBUG config option you'll see warnings appearing if this function returns an error and the buffers aren't requeued. It's a good test to do.
Quoted 165 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +165
-0+ +static int scaler_m2m_stop_streaming (struct vb2_queue *q) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); + +pm_runtime_put (&ctx->scaler_dev->pdev->dev); + +return 0 ; +} + +void scaler_m2m_job_finish (struct scaler_ctx *ctx,int vb_state) +{ +struct vb2_buffer *src_vb, *dst_vb; + +if (!ctx || !ctx->m2m_ctx) +return ; + + src_vb =v4l2_m2m_src_buf_remove (ctx->m2m_ctx); + dst_vb =v4l2_m2m_dst_buf_remove (ctx->m2m_ctx); + +if (src_vb && dst_vb) { +v4l2_m2m_buf_done (src_vb, vb_state); +v4l2_m2m_buf_done (dst_vb, vb_state); + +v4l2_m2m_job_finish (ctx->scaler_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +static void scaler_m2m_job_abort (void *priv) +{ +struct scaler_ctx *ctx = priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); +} + +static int scaler_get_bufs (struct scaler_ctx *ctx) +{ +struct scaler_frame *s_frame, *d_frame; +struct vb2_buffer *src_vb, *dst_vb; +int ret; + + s_frame = &ctx->s_frame; + d_frame = &ctx->d_frame; + + src_vb =v4l2_m2m_next_src_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, src_vb, s_frame, &s_frame->addr); +if (ret <0 ) +return ret; + + dst_vb =v4l2_m2m_next_dst_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, dst_vb, d_frame, &d_frame->addr); +if (ret <0 ) +return ret; + + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + +return 0 ; +} + +static void scaler_m2m_device_run (void *priv) +{ +struct scaler_ctx *ctx = priv; +struct scaler_dev *scaler; +unsigned long flags; +int ret; +bool is_stopped; + +if (WARN (!ctx,"Null hardware context\n" )) +return ; + + scaler = ctx->scaler_dev; +spin_lock_irqsave (&scaler->slock, flags); + +set_bit (ST_M2M_PEND , &scaler->state); + +/* Reconfigure hardware if the context has changed. */ +if (scaler->m2m.ctx != ctx) { +scaler_dbg (scaler,"scaler->m2m.ctx = 0x%p, current_ctx = 0x%p" , + scaler->m2m.ctx, ctx); + ctx->state |=SCALER_PARAMS ; + scaler->m2m.ctx = ctx; + } + + is_stopped = ctx->state &SCALER_CTX_STOP_REQ ; + ctx->state &= ~SCALER_CTX_STOP_REQ ; +if (is_stopped) { +wake_up (&scaler->irq_queue); + goto unlock; + } + + ret =scaler_get_bufs (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Wrong address\n" ); + goto unlock; + } + +scaler_hw_address_queue_reset (ctx); +scaler_set_prefbuf (scaler, &ctx->s_frame); +scaler_hw_set_input_addr (scaler, &ctx->s_frame.addr); +scaler_hw_set_output_addr (scaler, &ctx->d_frame.addr); +scaler_hw_set_csc_coeff (ctx); + +if (ctx->state &SCALER_PARAMS ) { +scaler_hw_set_irq (scaler,SCALER_INT_FRAME_END , false); +if (scaler_set_scaler_info (ctx)) { +scaler_dbg (scaler,"Scaler setup error" ); + goto unlock; + } + +scaler_hw_set_in_size (ctx); +scaler_hw_set_in_image_format (ctx); + +scaler_hw_set_out_size (ctx); +scaler_hw_set_out_image_format (ctx); + +scaler_hw_set_scaler_ratio (ctx); +scaler_hw_set_rotation (ctx); + } + + ctx->state &= ~SCALER_PARAMS ; +scaler_hw_enable_control (scaler, true); + +spin_unlock_irqrestore (&scaler->slock, flags); +return ; + +unlock: + ctx->state &= ~SCALER_PARAMS ; +spin_unlock_irqrestore (&scaler->slock, flags); +} + +static int scaler_m2m_queue_setup (struct vb2_queue *vq, +const struct v4l2_format *fmt, +unsigned int *num_buffers,unsigned int *num_planes, +unsigned int sizes[],void *allocators[]) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vq->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (!frame->fmt) +return -EINVAL ; + + *num_planes = frame->fmt->num_planes; +for (i =0 ; i < frame->fmt->num_planes; i++) { + sizes[i] = frame->payload[i]; + allocators[i] = ctx->scaler_dev->alloc_ctx; + } +return 0 ; +} + +static int scaler_m2m_buf_prepare (struct vb2_buffer *vb) +{
drivers/media/platform/exynos-scaler/scaler-m2m.c line 225
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vb->vb2_queue->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +for (i =0 ; i < frame->fmt->num_planes; i++) +vb2_set_plane_payload (vb, i, frame->payload[i]);
This sets the payload for buffers going to the hardware as well, but it should check whether the payload is large enough instead. I.e., if someone passes a buffer with too little data an error should be returned.
Quoted 26 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +26
-0+ +return 0 ; +} + +static void scaler_m2m_buf_queue (struct vb2_buffer *vb) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); + +scaler_dbg (ctx->scaler_dev,"ctx: %p, ctx->state: 0x%x" , + ctx, ctx->state); + +if (ctx->m2m_ctx) +v4l2_m2m_buf_queue (ctx->m2m_ctx, vb); +} + +static void scaler_lock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_lock (&ctx->scaler_dev->lock); +} + +static void scaler_unlock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_unlock (&ctx->scaler_dev->lock); +}
drivers/media/platform/exynos-scaler/scaler-m2m.c line 261
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+ +static struct vb2_ops scaler_m2m_qops = { + .queue_setup = scaler_m2m_queue_setup, + .buf_prepare = scaler_m2m_buf_prepare, + .buf_queue = scaler_m2m_buf_queue, + .wait_prepare = scaler_unlock, + .wait_finish = scaler_lock, + .stop_streaming = scaler_m2m_stop_streaming, + .start_streaming = scaler_m2m_start_streaming, +};
Have you tested this driver with the latest v4l2-compliance?
Just run 'v4l2-compliance -s' and it should work. If you find problems with v4l2-compliance itself, please let me know. It hasn't seen much testing with m2m drivers, so feedback is welcome.
Quoted 161 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +161
-0+ +static int scaler_m2m_querycap (struct file *file,void *fh, +struct v4l2_capability *cap) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; + + cap->device_caps =V4L2_CAP_STREAMING |V4L2_CAP_VIDEO_M2M_MPLANE ; + cap->capabilities = cap->device_caps |V4L2_CAP_DEVICE_CAPS ; + +strlcpy (cap->driver, scaler->pdev->name,sizeof (cap->driver)); +strlcpy (cap->card, scaler->pdev->name,sizeof (cap->card)); +strlcpy (cap->bus_info,"platform:exynos5-scaler" , +sizeof (cap->bus_info)); + +return 0 ; +} + +static int scaler_m2m_enum_fmt_mplane (struct file *file,void *priv, +struct v4l2_fmtdesc *f) +{ +return scaler_enum_fmt_mplane (f); +} + +static int scaler_m2m_g_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_g_fmt_mplane (ctx, f); +} + +static int scaler_m2m_try_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_try_fmt_mplane (ctx, f); +} + +static int scaler_m2m_set_default_format (struct scaler_ctx *ctx) +{ +struct scaler_frame *frame; +u32 def_pixformat =SCALER_DEF_PIX_FMT ; +int i; + + frame = &ctx->s_frame; + + frame->colorspace =V4L2_COLORSPACE_REC709 ; + frame->fmt =scaler_find_fmt (&def_pixformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +scaler_set_frame_size (frame,SCALER_DEF_WIDTH ,SCALER_DEF_HEIGHT ); +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] =SCALER_DEF_WIDTH *SCALER_DEF_HEIGHT * + (frame->fmt->depth[i] /8 ); + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +/* Apply the same src frame settings to dst frame */ + ctx->d_frame = ctx->s_frame; + +return 0 ; +} + + +static int scaler_m2m_s_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct vb2_queue *vq; +struct scaler_frame *frame; +struct v4l2_pix_format_mplane *pix; +int i, ret =0 ; + + ret =scaler_m2m_try_fmt_mplane (file, fh, f); +if (ret <0 ) +return ret; + + vq =v4l2_m2m_get_vq (ctx->m2m_ctx, f->type); + +if (vb2_is_streaming (vq)) { +scaler_dbg (ctx->scaler_dev,"queue (%d) busy" , f->type); +return -EBUSY ; + } + +if (V4L2_TYPE_IS_OUTPUT (f->type)) + frame = &ctx->s_frame; +else + frame = &ctx->d_frame; + + pix = &f->fmt.pix_mp; + frame->colorspace = pix->colorspace; + frame->fmt =scaler_find_fmt (&pix->pixelformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] = pix->plane_fmt[i].sizeimage; + +scaler_set_frame_size (frame, pix->width, pix->height); +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +scaler_dbg (ctx->scaler_dev,"f_w: %d, f_h: %d" , + frame->f_width, frame->f_height); + +return 0 ; +} + +static int scaler_m2m_reqbufs (struct file *file,void *fh, +struct v4l2_requestbuffers *reqbufs) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; +u32 max_cnt; + +if (reqbufs->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + max_cnt = scaler->variant->in_buf_cnt; +else + max_cnt = scaler->variant->out_buf_cnt; + +if (reqbufs->count > max_cnt) { +/* Adjust the count value as per driver supports */ + reqbufs->count = max_cnt; + } + +return v4l2_m2m_reqbufs (file, ctx->m2m_ctx, reqbufs); +} + +static int scaler_m2m_expbuf (struct file *file,void *fh, +struct v4l2_exportbuffer *eb) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_expbuf (file, ctx->m2m_ctx, eb); +} + +static int scaler_m2m_querybuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_querybuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_qbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_qbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_dqbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_dqbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_streamon (struct file *file,void *fh, +enum v4l2_buf_type type) +{
drivers/media/platform/exynos-scaler/scaler-m2m.c line 432
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamon (file, ctx->m2m_ctx, type); +} + +static int scaler_m2m_streamoff (struct file *file,void *fh, +enum v4l2_buf_type type) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamoff (file, ctx->m2m_ctx, type); +}
v4l2-mem2mem.h defines a bunch of ioctl and file-operation helpers that you can use directly rather than creating these small stubs.
Just set the m2m_ctx field of the v4l2_fh struct to ctx->m2m_ctx and you are good to go. This means that you need to do a 'container_of' to go from the m2m_ctx to the scaler_ctx pointer.
Quoted 347 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +346
-0+ +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int is_rectangle_enclosed (struct v4l2_rect *a,struct v4l2_rect *b) +{ +return !((a->left < b->left) || + (a->top < b->top) || + (a->left + a->width > b->left + b->width) || + (a->top + a->height > b->top + b->height)); +} + +static int scaler_m2m_g_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); + +if ((s->type !=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->type !=V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE )) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) { +if ((s->target ==V4L2_SEL_TGT_CROP_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_CROP_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_CROP ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + }else { +if ((s->target ==V4L2_SEL_TGT_COMPOSE_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_COMPOSE_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_COMPOSE ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + } + +return -EINVAL ; +} + +static int scaler_m2m_s_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct v4l2_crop cr; +struct scaler_variant *variant = ctx->scaler_dev->variant; +int ret; + + cr.type = s->type; + cr.c = s->r; + +if (!(((s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) && + (s->target ==V4L2_SEL_TGT_CROP )) || + ((s->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->target ==V4L2_SEL_TGT_COMPOSE )))) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); + + ret =scaler_try_crop (ctx, &cr); +if (ret <0 ) +return ret; + +if (s->flags &V4L2_SEL_FLAG_LE && + !is_rectangle_enclosed (&cr.c, &s->r)) +return -ERANGE ; + +if (s->flags &V4L2_SEL_FLAG_GE && + !is_rectangle_enclosed (&s->r, &cr.c)) +return -ERANGE ; + + s->r = cr.c; + +/* Check to see if scaling ratio is within supported range */ +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + ret =scaler_check_scaler_ratio (variant, + cr.c.width, cr.c.height, + ctx->d_frame.selection.width, + ctx->d_frame.selection.height, + ctx->ctrls_scaler.rotate->val); +else + ret =scaler_check_scaler_ratio (variant, + ctx->s_frame.selection.width, + ctx->s_frame.selection.height, + cr.c.width, cr.c.height, + ctx->ctrls_scaler.rotate->val); + +if (ret <0 ) { +scaler_dbg (ctx->scaler_dev,"Out of scaler range" ); +return -EINVAL ; + } + + frame->selection = cr.c; + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); +return 0 ; +} + +static const struct v4l2_ioctl_ops scaler_m2m_ioctl_ops = { + .vidioc_querycap = scaler_m2m_querycap, + .vidioc_enum_fmt_vid_cap_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_reqbufs = scaler_m2m_reqbufs, + .vidioc_expbuf = scaler_m2m_expbuf, + .vidioc_querybuf = scaler_m2m_querybuf, + .vidioc_qbuf = scaler_m2m_qbuf, + .vidioc_dqbuf = scaler_m2m_dqbuf, + .vidioc_streamon = scaler_m2m_streamon, + .vidioc_streamoff = scaler_m2m_streamoff, + .vidioc_g_selection = scaler_m2m_g_selection, + .vidioc_s_selection = scaler_m2m_s_selection +}; + +static int queue_init (void *priv,struct vb2_queue *src_vq, +struct vb2_queue *dst_vq) +{ +struct scaler_ctx *ctx = priv; +int ret; + +memset (src_vq,0 ,sizeof (*src_vq)); + src_vq->type =V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ; + src_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + src_vq->drv_priv = ctx; + src_vq->ops = &scaler_m2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + + ret =vb2_queue_init (src_vq); +if (ret <0 ) +return ret; + +memset (dst_vq,0 ,sizeof (*dst_vq)); + dst_vq->type =V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ; + dst_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + dst_vq->drv_priv = ctx; + dst_vq->ops = &scaler_m2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + +return vb2_queue_init (dst_vq); +} + +static int scaler_m2m_open (struct file *file) +{ +struct scaler_dev *scaler =video_drvdata (file); +struct scaler_ctx *ctx =NULL ; +int ret; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ctx =kzalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) { + ret = -ENOMEM ; + goto unlock; + } + +v4l2_fh_init (&ctx->fh, scaler->m2m.vfd); + ret =scaler_ctrls_create (ctx); +if (ret <0 ) + goto error_fh; + +/* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; +v4l2_fh_add (&ctx->fh); + + ctx->scaler_dev = scaler; +/* Default color format */ + ctx->s_frame.fmt =scaler_get_format (0 ); + ctx->d_frame.fmt =scaler_get_format (0 ); + ctx->flags =0 ; + + ctx->m2m_ctx =v4l2_m2m_ctx_init (scaler->m2m.m2m_dev, ctx, queue_init); +if (IS_ERR (ctx->m2m_ctx)) { +scaler_dbg (scaler,"Failed to initialize m2m context" ); + ret =PTR_ERR (ctx->m2m_ctx); + goto error_ctrls; + } + +/* Apply default format settings */ + ret =scaler_m2m_set_default_format (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Failed to set default format settings" ); + goto error_ctrls; + } + +if (v4l2_fh_is_singular_file (file)) +set_bit (ST_M2M_OPEN , &scaler->state); + +scaler_dbg (scaler,"scaler m2m driver is opened, ctx(0x%p)" , ctx); + +mutex_unlock (&scaler->lock); +return 0 ; + +error_ctrls: +scaler_ctrls_delete (ctx); +error_fh: +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); +kfree (ctx); +unlock: +mutex_unlock (&scaler->lock); +return ret; +} + +static int scaler_m2m_release (struct file *file) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +mutex_lock (&scaler->lock); + +if (v4l2_fh_is_singular_file (file)) +clear_bit (ST_M2M_OPEN , &scaler->state); + +v4l2_m2m_ctx_release (ctx->m2m_ctx); +scaler_ctrls_delete (ctx); +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); + +kfree (ctx); + +mutex_unlock (&scaler->lock); +return 0 ; +} + +static unsigned int scaler_m2m_poll (struct file *file, +struct poll_table_struct *wait) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_poll (file, ctx->m2m_ctx, wait); +mutex_unlock (&scaler->lock); + +return ret; +} + +static int scaler_m2m_mmap (struct file *file,struct vm_area_struct *vma) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_mmap (file, ctx->m2m_ctx, vma); +mutex_unlock (&scaler->lock); + +return ret; +} + +static const struct v4l2_file_operations scaler_m2m_fops = { + .owner =THIS_MODULE , + .open = scaler_m2m_open, + .release = scaler_m2m_release, + .poll = scaler_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = scaler_m2m_mmap, +}; + +static struct v4l2_m2m_ops scaler_m2m_ops = { + .device_run = scaler_m2m_device_run, + .job_abort = scaler_m2m_job_abort, +}; + +int scaler_register_m2m_device (struct scaler_dev *scaler) +{ +struct platform_device *pdev; +int ret; + +if (!scaler) +return -ENODEV ; + + pdev = scaler->pdev; + + scaler->vdev.fops = &scaler_m2m_fops; + scaler->vdev.ioctl_ops = &scaler_m2m_ioctl_ops; + scaler->vdev.release = video_device_release_empty; + scaler->vdev.lock = &scaler->lock; + scaler->vdev.vfl_dir =VFL_DIR_M2M ; + scaler->vdev.v4l2_dev = &scaler->v4l2_dev; +snprintf (scaler->vdev.name,sizeof (scaler->vdev.name),"%s:m2m" , +SCALER_MODULE_NAME ); + +video_set_drvdata (&scaler->vdev, scaler); + + scaler->m2m.vfd = &scaler->vdev; + scaler->m2m.m2m_dev =v4l2_m2m_init (&scaler_m2m_ops); +if (IS_ERR (scaler->m2m.m2m_dev)) { +dev_err (&pdev->dev,"failed to initialize v4l2-m2m device\n" ); +return PTR_ERR (scaler->m2m.m2m_dev); + } + + ret =video_register_device (&scaler->vdev,VFL_TYPE_GRABBER ,-1 ); +if (ret <0 ) { +dev_err (&pdev->dev, +"%s(): failed to register video device\n" , __func__); +v4l2_m2m_release (scaler->m2m.m2m_dev); +return ret; + } + +dev_info (&pdev->dev,"scaler m2m driver registered as /dev/video%d" , + scaler->vdev.num);
drivers/media/platform/exynos-scaler/scaler-m2m.c line 788
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+return 0 ; +} + +void scaler_unregister_m2m_device (struct scaler_dev *scaler) +{ +if (scaler) { +video_unregister_device (scaler->m2m.vfd); +v4l2_m2m_release (scaler->m2m.m2m_dev); + } +}
Regards,
Hans
PATCH v6 3/4 exynos-scaler: Add m2m functionality for the SCALER driver
Hi Hans,
Thanks for the review comments.
Frankly, I was not following the recent changes related to vb2 framework. And I didn't tested the driver for v4l2-compliance :(
Will try to address all your comments in the next version of posting and this time with v4l2-compliance :)
Regards,
Shaik Ameer Basha
Quoted 855 lines · Hans Verkuil
Thanks for this patch series!
I've got some comments below...
Regards,
Hans
Quoted 59 lines · Shaik Ameer Basha
This patch adds memory to memory (m2m) interface functionality for the SCALER driver.
[arun.kk at samsung.com: fix compilation issues]
Signed-off-by Shaik Ameer Basha <[email protected]>Signed-off-by Arun Kumar K <[email protected]>Reviewed-by Sylwester Nawrocki <[email protected]>--- drivers/media/platform/exynos-scaler/scaler-m2m.c | 788 +++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.cdrivers/media/platform/exynos-scaler/scaler-m2m.c new file +40
-0@@ -0,0 +1,788 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* http://www.samsung.com +* +* Samsung EXYNOS5 SoC series SCALER driver +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <media/v4l2-ioctl.h> + +#include "scaler-regs.h" + +#define SCALER_DEF_PIX_FMT V4L2_PIX_FMT_RGB32 +#define SCALER_DEF_WIDTH 1280 +#define SCALER_DEF_HEIGHT 720 + +static int scaler_m2m_ctx_stop_req (struct scaler_ctx *ctx) +{ +struct scaler_ctx *curr_ctx; +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + + curr_ctx =v4l2_m2m_get_curr_priv (scaler->m2m.m2m_dev); +if (!scaler_m2m_pending (scaler) || (curr_ctx != ctx)) +return 0 ; + +scaler_ctx_state_lock_set (SCALER_CTX_STOP_REQ , ctx); + ret =wait_event_timeout (scaler->irq_queue, + !scaler_ctx_state_is_set (SCALER_CTX_STOP_REQ , ctx), +SCALER_SHUTDOWN_TIMEOUT ); + +return ret ==0 ? -ETIMEDOUT : ret; +}drivers/media/platform/exynos-scaler/scaler-m2m.c line 50
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+ +static int scaler_m2m_start_streaming (struct vb2_queue *q,unsigned int count) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =pm_runtime_get_sync (&ctx->scaler_dev->pdev->dev); + +return ret >0 ?0 : ret; +}If an error occurs, then all queued buffers must be returned to the vb2 core with
vb2_buffer_donewith stateVB2_BUF_STATE_QUEUED. A lot of vb2 instrumentation has been added in recent weeks to check for correct buffer handling, and this was one of the corner cases that was never handled correctly. If you compile with theVIDEO_ADV_DEBUGconfig option you'll see warnings appearing if this function returns an error and the buffers aren't requeued. It's a good test to do.Quoted 165 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +165
-0+ +static int scaler_m2m_stop_streaming (struct vb2_queue *q) +{ +struct scaler_ctx *ctx = q->drv_priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); + +pm_runtime_put (&ctx->scaler_dev->pdev->dev); + +return 0 ; +} + +void scaler_m2m_job_finish (struct scaler_ctx *ctx,int vb_state) +{ +struct vb2_buffer *src_vb, *dst_vb; + +if (!ctx || !ctx->m2m_ctx) +return ; + + src_vb =v4l2_m2m_src_buf_remove (ctx->m2m_ctx); + dst_vb =v4l2_m2m_dst_buf_remove (ctx->m2m_ctx); + +if (src_vb && dst_vb) { +v4l2_m2m_buf_done (src_vb, vb_state); +v4l2_m2m_buf_done (dst_vb, vb_state); + +v4l2_m2m_job_finish (ctx->scaler_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +static void scaler_m2m_job_abort (void *priv) +{ +struct scaler_ctx *ctx = priv; +int ret; + + ret =scaler_m2m_ctx_stop_req (ctx); +if (ret <0 ) +scaler_m2m_job_finish (ctx,VB2_BUF_STATE_ERROR ); +} + +static int scaler_get_bufs (struct scaler_ctx *ctx) +{ +struct scaler_frame *s_frame, *d_frame; +struct vb2_buffer *src_vb, *dst_vb; +int ret; + + s_frame = &ctx->s_frame; + d_frame = &ctx->d_frame; + + src_vb =v4l2_m2m_next_src_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, src_vb, s_frame, &s_frame->addr); +if (ret <0 ) +return ret; + + dst_vb =v4l2_m2m_next_dst_buf (ctx->m2m_ctx); + ret =scaler_prepare_addr (ctx, dst_vb, d_frame, &d_frame->addr); +if (ret <0 ) +return ret; + + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + +return 0 ; +} + +static void scaler_m2m_device_run (void *priv) +{ +struct scaler_ctx *ctx = priv; +struct scaler_dev *scaler; +unsigned long flags; +int ret; +bool is_stopped; + +if (WARN (!ctx,"Null hardware context\n" )) +return ; + + scaler = ctx->scaler_dev; +spin_lock_irqsave (&scaler->slock, flags); + +set_bit (ST_M2M_PEND , &scaler->state); + +/* Reconfigure hardware if the context has changed. */ +if (scaler->m2m.ctx != ctx) { +scaler_dbg (scaler,"scaler->m2m.ctx = 0x%p, current_ctx = 0x%p" , + scaler->m2m.ctx, ctx); + ctx->state |=SCALER_PARAMS ; + scaler->m2m.ctx = ctx; + } + + is_stopped = ctx->state &SCALER_CTX_STOP_REQ ; + ctx->state &= ~SCALER_CTX_STOP_REQ ; +if (is_stopped) { +wake_up (&scaler->irq_queue); + goto unlock; + } + + ret =scaler_get_bufs (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Wrong address\n" ); + goto unlock; + } + +scaler_hw_address_queue_reset (ctx); +scaler_set_prefbuf (scaler, &ctx->s_frame); +scaler_hw_set_input_addr (scaler, &ctx->s_frame.addr); +scaler_hw_set_output_addr (scaler, &ctx->d_frame.addr); +scaler_hw_set_csc_coeff (ctx); + +if (ctx->state &SCALER_PARAMS ) { +scaler_hw_set_irq (scaler,SCALER_INT_FRAME_END , false); +if (scaler_set_scaler_info (ctx)) { +scaler_dbg (scaler,"Scaler setup error" ); + goto unlock; + } + +scaler_hw_set_in_size (ctx); +scaler_hw_set_in_image_format (ctx); + +scaler_hw_set_out_size (ctx); +scaler_hw_set_out_image_format (ctx); + +scaler_hw_set_scaler_ratio (ctx); +scaler_hw_set_rotation (ctx); + } + + ctx->state &= ~SCALER_PARAMS ; +scaler_hw_enable_control (scaler, true); + +spin_unlock_irqrestore (&scaler->slock, flags); +return ; + +unlock: + ctx->state &= ~SCALER_PARAMS ; +spin_unlock_irqrestore (&scaler->slock, flags); +} + +static int scaler_m2m_queue_setup (struct vb2_queue *vq, +const struct v4l2_format *fmt, +unsigned int *num_buffers,unsigned int *num_planes, +unsigned int sizes[],void *allocators[]) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vq->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (!frame->fmt) +return -EINVAL ; + + *num_planes = frame->fmt->num_planes; +for (i =0 ; i < frame->fmt->num_planes; i++) { + sizes[i] = frame->payload[i]; + allocators[i] = ctx->scaler_dev->alloc_ctx; + } +return 0 ; +} + +static int scaler_m2m_buf_prepare (struct vb2_buffer *vb) +{drivers/media/platform/exynos-scaler/scaler-m2m.c line 225
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); +struct scaler_frame *frame; +int i; + + frame =ctx_get_frame (ctx, vb->vb2_queue->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +for (i =0 ; i < frame->fmt->num_planes; i++) +vb2_set_plane_payload (vb, i, frame->payload[i]);This sets the payload for buffers going to the hardware as well, but it should check whether the payload is large enough instead. I.e., if someone passes a buffer with too little data an error should be returned.
Quoted 26 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +26
-0+ +return 0 ; +} + +static void scaler_m2m_buf_queue (struct vb2_buffer *vb) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vb->vb2_queue); + +scaler_dbg (ctx->scaler_dev,"ctx: %p, ctx->state: 0x%x" , + ctx, ctx->state); + +if (ctx->m2m_ctx) +v4l2_m2m_buf_queue (ctx->m2m_ctx, vb); +} + +static void scaler_lock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_lock (&ctx->scaler_dev->lock); +} + +static void scaler_unlock (struct vb2_queue *vq) +{ +struct scaler_ctx *ctx =vb2_get_drv_priv (vq); +mutex_unlock (&ctx->scaler_dev->lock); +}drivers/media/platform/exynos-scaler/scaler-m2m.c line 261
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+ +static struct vb2_ops scaler_m2m_qops = { + .queue_setup = scaler_m2m_queue_setup, + .buf_prepare = scaler_m2m_buf_prepare, + .buf_queue = scaler_m2m_buf_queue, + .wait_prepare = scaler_unlock, + .wait_finish = scaler_lock, + .stop_streaming = scaler_m2m_stop_streaming, + .start_streaming = scaler_m2m_start_streaming, +};Have you tested this driver with the latest v4l2-compliance?
Just run 'v4l2-compliance -s' and it should work. If you find problems with v4l2-compliance itself, please let me know. It hasn't seen much testing with m2m drivers, so feedback is welcome.
Quoted 161 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +161
-0+ +static int scaler_m2m_querycap (struct file *file,void *fh, +struct v4l2_capability *cap) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; + + cap->device_caps =V4L2_CAP_STREAMING |V4L2_CAP_VIDEO_M2M_MPLANE ; + cap->capabilities = cap->device_caps |V4L2_CAP_DEVICE_CAPS ; + +strlcpy (cap->driver, scaler->pdev->name,sizeof (cap->driver)); +strlcpy (cap->card, scaler->pdev->name,sizeof (cap->card)); +strlcpy (cap->bus_info,"platform:exynos5-scaler" , +sizeof (cap->bus_info)); + +return 0 ; +} + +static int scaler_m2m_enum_fmt_mplane (struct file *file,void *priv, +struct v4l2_fmtdesc *f) +{ +return scaler_enum_fmt_mplane (f); +} + +static int scaler_m2m_g_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_g_fmt_mplane (ctx, f); +} + +static int scaler_m2m_try_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); + +return scaler_try_fmt_mplane (ctx, f); +} + +static int scaler_m2m_set_default_format (struct scaler_ctx *ctx) +{ +struct scaler_frame *frame; +u32 def_pixformat =SCALER_DEF_PIX_FMT ; +int i; + + frame = &ctx->s_frame; + + frame->colorspace =V4L2_COLORSPACE_REC709 ; + frame->fmt =scaler_find_fmt (&def_pixformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +scaler_set_frame_size (frame,SCALER_DEF_WIDTH ,SCALER_DEF_HEIGHT ); +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] =SCALER_DEF_WIDTH *SCALER_DEF_HEIGHT * + (frame->fmt->depth[i] /8 ); + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +/* Apply the same src frame settings to dst frame */ + ctx->d_frame = ctx->s_frame; + +return 0 ; +} + + +static int scaler_m2m_s_fmt_mplane (struct file *file,void *fh, +struct v4l2_format *f) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct vb2_queue *vq; +struct scaler_frame *frame; +struct v4l2_pix_format_mplane *pix; +int i, ret =0 ; + + ret =scaler_m2m_try_fmt_mplane (file, fh, f); +if (ret <0 ) +return ret; + + vq =v4l2_m2m_get_vq (ctx->m2m_ctx, f->type); + +if (vb2_is_streaming (vq)) { +scaler_dbg (ctx->scaler_dev,"queue (%d) busy" , f->type); +return -EBUSY ; + } + +if (V4L2_TYPE_IS_OUTPUT (f->type)) + frame = &ctx->s_frame; +else + frame = &ctx->d_frame; + + pix = &f->fmt.pix_mp; + frame->colorspace = pix->colorspace; + frame->fmt =scaler_find_fmt (&pix->pixelformat,-1 ); +if (!frame->fmt) +return -EINVAL ; + +for (i =0 ; i < frame->fmt->num_planes; i++) + frame->payload[i] = pix->plane_fmt[i].sizeimage; + +scaler_set_frame_size (frame, pix->width, pix->height); +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); + +scaler_dbg (ctx->scaler_dev,"f_w: %d, f_h: %d" , + frame->f_width, frame->f_height); + +return 0 ; +} + +static int scaler_m2m_reqbufs (struct file *file,void *fh, +struct v4l2_requestbuffers *reqbufs) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct scaler_dev *scaler = ctx->scaler_dev; +u32 max_cnt; + +if (reqbufs->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + max_cnt = scaler->variant->in_buf_cnt; +else + max_cnt = scaler->variant->out_buf_cnt; + +if (reqbufs->count > max_cnt) { +/* Adjust the count value as per driver supports */ + reqbufs->count = max_cnt; + } + +return v4l2_m2m_reqbufs (file, ctx->m2m_ctx, reqbufs); +} + +static int scaler_m2m_expbuf (struct file *file,void *fh, +struct v4l2_exportbuffer *eb) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_expbuf (file, ctx->m2m_ctx, eb); +} + +static int scaler_m2m_querybuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_querybuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_qbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_qbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_dqbuf (struct file *file,void *fh, +struct v4l2_buffer *buf) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_dqbuf (file, ctx->m2m_ctx, buf); +} + +static int scaler_m2m_streamon (struct file *file,void *fh, +enum v4l2_buf_type type) +{drivers/media/platform/exynos-scaler/scaler-m2m.c line 432
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamon (file, ctx->m2m_ctx, type); +} + +static int scaler_m2m_streamoff (struct file *file,void *fh, +enum v4l2_buf_type type) +{ +struct scaler_ctx *ctx =fh_to_ctx (fh); +return v4l2_m2m_streamoff (file, ctx->m2m_ctx, type); +}
v4l2-mem2mem.hdefines a bunch of ioctl and file-operation helpers that you can use directly rather than creating these small stubs.Just set the
m2m_ctxfield of thev4l2_fhstruct toctx->m2m_ctxand you are good to go. This means that you need to do a 'container_of' to go from them2m_ctxto thescaler_ctxpointer.Quoted 347 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +346
-0+ +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int is_rectangle_enclosed (struct v4l2_rect *a,struct v4l2_rect *b) +{ +return !((a->left < b->left) || + (a->top < b->top) || + (a->left + a->width > b->left + b->width) || + (a->top + a->height > b->top + b->height)); +} + +static int scaler_m2m_g_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); + +if ((s->type !=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->type !=V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE )) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); +if (IS_ERR (frame)) +return PTR_ERR (frame); + +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) { +if ((s->target ==V4L2_SEL_TGT_CROP_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_CROP_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_CROP ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + }else { +if ((s->target ==V4L2_SEL_TGT_COMPOSE_DEFAULT ) || + (s->target ==V4L2_SEL_TGT_COMPOSE_BOUNDS )) { + s->r.left =0 ; + s->r.top =0 ; + s->r.width = frame->f_width; + s->r.height = frame->f_height; +return 0 ; + } + +if (s->target ==V4L2_SEL_TGT_COMPOSE ) { + s->r.left = frame->selection.left; + s->r.top = frame->selection.top; + s->r.width = frame->selection.width; + s->r.height = frame->selection.height; +return 0 ; + } + } + +return -EINVAL ; +} + +static int scaler_m2m_s_selection (struct file *file,void *fh, +struct v4l2_selection *s) +{ +struct scaler_frame *frame; +struct scaler_ctx *ctx =fh_to_ctx (fh); +struct v4l2_crop cr; +struct scaler_variant *variant = ctx->scaler_dev->variant; +int ret; + + cr.type = s->type; + cr.c = s->r; + +if (!(((s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) && + (s->target ==V4L2_SEL_TGT_CROP )) || + ((s->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) && + (s->target ==V4L2_SEL_TGT_COMPOSE )))) +return -EINVAL ; + + frame =ctx_get_frame (ctx, s->type); + + ret =scaler_try_crop (ctx, &cr); +if (ret <0 ) +return ret; + +if (s->flags &V4L2_SEL_FLAG_LE && + !is_rectangle_enclosed (&cr.c, &s->r)) +return -ERANGE ; + +if (s->flags &V4L2_SEL_FLAG_GE && + !is_rectangle_enclosed (&s->r, &cr.c)) +return -ERANGE ; + + s->r = cr.c; + +/* Check to see if scaling ratio is within supported range */ +if (s->type ==V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) + ret =scaler_check_scaler_ratio (variant, + cr.c.width, cr.c.height, + ctx->d_frame.selection.width, + ctx->d_frame.selection.height, + ctx->ctrls_scaler.rotate->val); +else + ret =scaler_check_scaler_ratio (variant, + ctx->s_frame.selection.width, + ctx->s_frame.selection.height, + cr.c.width, cr.c.height, + ctx->ctrls_scaler.rotate->val); + +if (ret <0 ) { +scaler_dbg (ctx->scaler_dev,"Out of scaler range" ); +return -EINVAL ; + } + + frame->selection = cr.c; + +scaler_ctx_state_lock_set (SCALER_PARAMS , ctx); +return 0 ; +} + +static const struct v4l2_ioctl_ops scaler_m2m_ioctl_ops = { + .vidioc_querycap = scaler_m2m_querycap, + .vidioc_enum_fmt_vid_cap_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = scaler_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = scaler_m2m_g_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = scaler_m2m_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = scaler_m2m_s_fmt_mplane, + .vidioc_reqbufs = scaler_m2m_reqbufs, + .vidioc_expbuf = scaler_m2m_expbuf, + .vidioc_querybuf = scaler_m2m_querybuf, + .vidioc_qbuf = scaler_m2m_qbuf, + .vidioc_dqbuf = scaler_m2m_dqbuf, + .vidioc_streamon = scaler_m2m_streamon, + .vidioc_streamoff = scaler_m2m_streamoff, + .vidioc_g_selection = scaler_m2m_g_selection, + .vidioc_s_selection = scaler_m2m_s_selection +}; + +static int queue_init (void *priv,struct vb2_queue *src_vq, +struct vb2_queue *dst_vq) +{ +struct scaler_ctx *ctx = priv; +int ret; + +memset (src_vq,0 ,sizeof (*src_vq)); + src_vq->type =V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ; + src_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + src_vq->drv_priv = ctx; + src_vq->ops = &scaler_m2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + + ret =vb2_queue_init (src_vq); +if (ret <0 ) +return ret; + +memset (dst_vq,0 ,sizeof (*dst_vq)); + dst_vq->type =V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ; + dst_vq->io_modes =VB2_MMAP |VB2_USERPTR |VB2_DMABUF ; + dst_vq->drv_priv = ctx; + dst_vq->ops = &scaler_m2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size =sizeof (struct v4l2_m2m_buffer ); + +return vb2_queue_init (dst_vq); +} + +static int scaler_m2m_open (struct file *file) +{ +struct scaler_dev *scaler =video_drvdata (file); +struct scaler_ctx *ctx =NULL ; +int ret; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ctx =kzalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) { + ret = -ENOMEM ; + goto unlock; + } + +v4l2_fh_init (&ctx->fh, scaler->m2m.vfd); + ret =scaler_ctrls_create (ctx); +if (ret <0 ) + goto error_fh; + +/* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; +v4l2_fh_add (&ctx->fh); + + ctx->scaler_dev = scaler; +/* Default color format */ + ctx->s_frame.fmt =scaler_get_format (0 ); + ctx->d_frame.fmt =scaler_get_format (0 ); + ctx->flags =0 ; + + ctx->m2m_ctx =v4l2_m2m_ctx_init (scaler->m2m.m2m_dev, ctx, queue_init); +if (IS_ERR (ctx->m2m_ctx)) { +scaler_dbg (scaler,"Failed to initialize m2m context" ); + ret =PTR_ERR (ctx->m2m_ctx); + goto error_ctrls; + } + +/* Apply default format settings */ + ret =scaler_m2m_set_default_format (ctx); +if (ret <0 ) { +scaler_dbg (scaler,"Failed to set default format settings" ); + goto error_ctrls; + } + +if (v4l2_fh_is_singular_file (file)) +set_bit (ST_M2M_OPEN , &scaler->state); + +scaler_dbg (scaler,"scaler m2m driver is opened, ctx(0x%p)" , ctx); + +mutex_unlock (&scaler->lock); +return 0 ; + +error_ctrls: +scaler_ctrls_delete (ctx); +error_fh: +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); +kfree (ctx); +unlock: +mutex_unlock (&scaler->lock); +return ret; +} + +static int scaler_m2m_release (struct file *file) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; + +scaler_dbg (scaler,"pid: %d, state: 0x%lx" , +task_pid_nr (current), scaler->state); + +mutex_lock (&scaler->lock); + +if (v4l2_fh_is_singular_file (file)) +clear_bit (ST_M2M_OPEN , &scaler->state); + +v4l2_m2m_ctx_release (ctx->m2m_ctx); +scaler_ctrls_delete (ctx); +v4l2_fh_del (&ctx->fh); +v4l2_fh_exit (&ctx->fh); + +kfree (ctx); + +mutex_unlock (&scaler->lock); +return 0 ; +} + +static unsigned int scaler_m2m_poll (struct file *file, +struct poll_table_struct *wait) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_poll (file, ctx->m2m_ctx, wait); +mutex_unlock (&scaler->lock); + +return ret; +} + +static int scaler_m2m_mmap (struct file *file,struct vm_area_struct *vma) +{ +struct scaler_ctx *ctx =fh_to_ctx (file->private_data); +struct scaler_dev *scaler = ctx->scaler_dev; +int ret; + +if (mutex_lock_interruptible (&scaler->lock)) +return -ERESTARTSYS ; + + ret =v4l2_m2m_mmap (file, ctx->m2m_ctx, vma); +mutex_unlock (&scaler->lock); + +return ret; +} + +static const struct v4l2_file_operations scaler_m2m_fops = { + .owner =THIS_MODULE , + .open = scaler_m2m_open, + .release = scaler_m2m_release, + .poll = scaler_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = scaler_m2m_mmap, +}; + +static struct v4l2_m2m_ops scaler_m2m_ops = { + .device_run = scaler_m2m_device_run, + .job_abort = scaler_m2m_job_abort, +}; + +int scaler_register_m2m_device (struct scaler_dev *scaler) +{ +struct platform_device *pdev; +int ret; + +if (!scaler) +return -ENODEV ; + + pdev = scaler->pdev; + + scaler->vdev.fops = &scaler_m2m_fops; + scaler->vdev.ioctl_ops = &scaler_m2m_ioctl_ops; + scaler->vdev.release = video_device_release_empty; + scaler->vdev.lock = &scaler->lock; + scaler->vdev.vfl_dir =VFL_DIR_M2M ; + scaler->vdev.v4l2_dev = &scaler->v4l2_dev; +snprintf (scaler->vdev.name,sizeof (scaler->vdev.name),"%s:m2m" , +SCALER_MODULE_NAME ); + +video_set_drvdata (&scaler->vdev, scaler); + + scaler->m2m.vfd = &scaler->vdev; + scaler->m2m.m2m_dev =v4l2_m2m_init (&scaler_m2m_ops); +if (IS_ERR (scaler->m2m.m2m_dev)) { +dev_err (&pdev->dev,"failed to initialize v4l2-m2m device\n" ); +return PTR_ERR (scaler->m2m.m2m_dev); + } + + ret =video_register_device (&scaler->vdev,VFL_TYPE_GRABBER ,-1 ); +if (ret <0 ) { +dev_err (&pdev->dev, +"%s(): failed to register video device\n" , __func__); +v4l2_m2m_release (scaler->m2m.m2m_dev); +return ret; + } + +dev_info (&pdev->dev,"scaler m2m driver registered as /dev/video%d" , + scaler->vdev.num);drivers/media/platform/exynos-scaler/scaler-m2m.c line 788
Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +10
-0+return 0 ; +} + +void scaler_unregister_m2m_device (struct scaler_dev *scaler) +{ +if (scaler) { +video_unregister_device (scaler->m2m.vfd); +v4l2_m2m_release (scaler->m2m.m2m_dev); + } +}Regards,
Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v6 4/4 exynos-scaler: Add core functionality for the SCALER driver
In reply to Shaik Ameer BashaThis patch adds the Makefile and core functionality for the SCALER driver.
--- drivers/media/platform/Kconfig | 8 + drivers/media/platform/Makefile | 1 + drivers/media/platform/exynos-scaler/Makefile | 3 + drivers/media/platform/exynos-scaler/scaler.c | 1235 +++++++++++++++++++++++++ drivers/media/platform/exynos-scaler/scaler.h | 376 ++++++++ 5 files changed, 1623 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/Makefile create mode 100644 drivers/media/platform/exynos-scaler/scaler.c create mode 100644 drivers/media/platform/exynos-scaler/scaler.h
drivers/media/platform/Kconfig +8 -0
@@ -196,6 +196,14 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_SAMSUNG_EXYNOS_SCALER + tristate "Samsung Exynos SCALER driver" + depends on VIDEO_DEV && VIDEO_V4L2 && (SOC_EXYNOS5410 || SOC_EXYNOS5420) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung EXYNOS5410/5420 SoC SCALER. + config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
drivers/media/platform/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ +obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_SCALER) += exynos-scaler/ obj-$(CONFIG_BLACKFIN) += blackfin/
drivers/media/platform/exynos-scaler/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +exynos-scaler-objs := scaler.o scaler-m2m.o scaler-regs.o + +obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_SCALER) += exynos-scaler.o
drivers/media/platform/exynos-scaler/scaler.c new file +1235 -0
1,235 lines not on this page · raw message
drivers/media/platform/exynos-scaler/scaler.h new file +376 -0
376 lines not on this page · raw message
Signature · 1 line
1.7.9.5