* [PATCH v5 0/4] Exynos5 Series SCALER Driver
@ 2014-01-09 3:28 Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER Shaik Ameer Basha
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-09 3:28 UTC (permalink / raw)
To: linux-arm-kernel
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 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 new driver for Exynos5 SCALER
[media] exynos-scaler: Add core functionality for the SCALER driver
[media] exynos-scaler: Add m2m functionality for the SCALER driver
[media] exynos-scaler: Add DT bindings for SCALER driver
.../devicetree/bindings/media/exynos5-scaler.txt | 22 +
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 | 1231 ++++++++++++++++++++
drivers/media/platform/exynos-scaler/scaler.h | 376 ++++++
9 files changed, 3097 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
--
1.7.9.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER
2014-01-09 3:28 [PATCH v5 0/4] Exynos5 Series SCALER Driver Shaik Ameer Basha
@ 2014-01-09 3:28 ` Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver Shaik Ameer Basha
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-09 3:28 UTC (permalink / raw)
To: linux-arm-kernel
This 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
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
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
diff --git a/drivers/media/platform/exynos-scaler/scaler-regs.c b/drivers/media/platform/exynos-scaler/scaler-regs.c
new file mode 100644
index 0000000..8f5076a
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/scaler-regs.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2013 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);
+}
diff --git a/drivers/media/platform/exynos-scaler/scaler-regs.h b/drivers/media/platform/exynos-scaler/scaler-regs.h
new file mode 100644
index 0000000..1ea9d3e
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/scaler-regs.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef REGS_SCALER_H_
+#define REGS_SCALER_H_
+
+#include "scaler.h"
+
+/* SCALER status */
+#define SCALER_STATUS 0x00
+#define SCALER_STATUS_RUNNING (1 << 1)
+#define SCALER_STATUS_READY_CLK_DOWN (1 << 0)
+
+/* SCALER config */
+#define SCALER_CFG 0x04
+#define SCALER_CFG_FILL_EN (1 << 24)
+#define SCALER_CFG_BLEND_CLR_DIV_ALPHA_EN (1 << 17)
+#define SCALER_CFG_BLEND_EN (1 << 16)
+#define SCALER_CFG_CSC_Y_OFFSET_SRC_EN (1 << 10)
+#define SCALER_CFG_CSC_Y_OFFSET_DST_EN (1 << 9)
+#define SCALER_CFG_16_BURST_MODE (1 << 8)
+#define SCALER_CFG_SOFT_RESET (1 << 1)
+#define SCALER_CFG_START_CMD (1 << 0)
+
+/* SCALER interrupts */
+#define SCALER_INT_TIMEOUT 31
+#define SCALER_INT_ILLEGAL_BLEND 24
+#define SCALER_INT_ILLEGAL_RATIO 23
+#define SCALER_INT_ILLEGAL_DST_HEIGHT 22
+#define SCALER_INT_ILLEGAL_DST_WIDTH 21
+#define SCALER_INT_ILLEGAL_DST_V_POS 20
+#define SCALER_INT_ILLEGAL_DST_H_POS 19
+#define SCALER_INT_ILLEGAL_DST_C_SPAN 18
+#define SCALER_INT_ILLEGAL_DST_Y_SPAN 17
+#define SCALER_INT_ILLEGAL_DST_CR_BASE 16
+#define SCALER_INT_ILLEGAL_DST_CB_BASE 15
+#define SCALER_INT_ILLEGAL_DST_Y_BASE 14
+#define SCALER_INT_ILLEGAL_DST_COLOR 13
+#define SCALER_INT_ILLEGAL_SRC_HEIGHT 12
+#define SCALER_INT_ILLEGAL_SRC_WIDTH 11
+#define SCALER_INT_ILLEGAL_SRC_CV_POS 10
+#define SCALER_INT_ILLEGAL_SRC_CH_POS 9
+#define SCALER_INT_ILLEGAL_SRC_YV_POS 8
+#define SCALER_INT_ILLEGAL_SRC_YH_POS 7
+#define SCALER_INT_ILLEGAL_SRC_C_SPAN 6
+#define SCALER_INT_ILLEGAL_SRC_Y_SPAN 5
+#define SCALER_INT_ILLEGAL_SRC_CR_BASE 4
+#define SCALER_INT_ILLEGAL_SRC_CB_BASE 3
+#define SCALER_INT_ILLEGAL_SRC_Y_BASE 2
+#define SCALER_INT_ILLEGAL_SRC_COLOR 1
+#define SCALER_INT_FRAME_END 0
+
+/* SCALER interrupt enable */
+#define SCALER_INT_EN 0x08
+#define SCALER_INT_EN_DEFAULT 0x81ffffff
+
+/* SCALER interrupt status */
+#define SCALER_INT_STATUS 0x0c
+#define SCALER_INT_STATUS_CLEAR 0xffffffff
+#define SCALER_INT_STATUS_ERROR 0x81fffffe
+
+/* SCALER source format configuration */
+#define SCALER_SRC_CFG 0x10
+#define SCALER_SRC_TILE_EN (0x1 << 10)
+#define SCALER_SRC_BYTE_SWAP_MASK 0x3
+#define SCALER_SRC_BYTE_SWAP_SHIFT 5
+#define SCALER_SRC_COLOR_FORMAT_MASK 0xf
+#define SCALER_SRC_COLOR_FORMAT_SHIFT 0
+
+/* SCALER source y-base */
+#define SCALER_SRC_Y_BASE 0x14
+
+/* SCALER source cb-base */
+#define SCALER_SRC_CB_BASE 0x18
+
+/* SCALER source cr-base */
+#define SCALER_SRC_CR_BASE 0x294
+
+/* SCALER source span */
+#define SCALER_SRC_SPAN 0x1c
+#define SCALER_SRC_C_SPAN_MASK 0x3fff
+#define SCALER_SRC_C_SPAN_SHIFT 16
+#define SCALER_SRC_Y_SPAN_MASK 0x3fff
+#define SCALER_SRC_Y_SPAN_SHIFT 0
+
+/*
+ * SCALER source y-position
+ * 14.2 fixed-point format
+ * - 14 bits at the MSB are for the integer part.
+ * - 2 bits at LSB are for fractional part and always has to be set to 0.
+ */
+#define SCALER_SRC_Y_POS 0x20
+#define SCALER_SRC_YH_POS_MASK 0xfffc
+#define SCALER_SRC_YH_POS_SHIFT 16
+#define SCALER_SRC_YV_POS_MASK 0xfffc
+#define SCALER_SRC_YV_POS_SHIFT 0
+
+/* SCALER source width/height */
+#define SCALER_SRC_WH 0x24
+#define SCALER_SRC_WIDTH_MASK 0x3fff
+#define SCALER_SRC_WIDTH_SHIFT 16
+#define SCALER_SRC_HEIGHT_MASK 0x3fff
+#define SCALER_SRC_HEIGHT_SHIFT 0
+
+/*
+ * SCALER source c-position
+ * 14.2 fixed-point format
+ * - 14 bits at the MSB are for the integer part.
+ * - 2 bits@LSB are for fractional part and always has to be set to 0.
+ */
+#define SCALER_SRC_C_POS 0x28
+#define SCALER_SRC_CH_POS_MASK 0xfffc
+#define SCALER_SRC_CH_POS_SHIFT 16
+#define SCALER_SRC_CV_POS_MASK 0xfffc
+#define SCALER_SRC_CV_POS_SHIFT 0
+
+/* SCALER destination format configuration */
+#define SCALER_DST_CFG 0x30
+#define SCALER_DST_BYTE_SWAP_MASK 0x3
+#define SCALER_DST_BYTE_SWAP_SHIFT 5
+#define SCALER_DST_COLOR_FORMAT_MASK 0xf
+
+/* SCALER destination y-base */
+#define SCALER_DST_Y_BASE 0x34
+
+/* SCALER destination cb-base */
+#define SCALER_DST_CB_BASE 0x38
+
+/* SCALER destination cr-base */
+#define SCALER_DST_CR_BASE 0x298
+
+/* SCALER destination span */
+#define SCALER_DST_SPAN 0x3c
+#define SCALER_DST_C_SPAN_MASK 0x3fff
+#define SCALER_DST_C_SPAN_SHIFT 16
+#define SCALER_DST_Y_SPAN_MASK 0x3fff
+#define SCALER_DST_Y_SPAN_SHIFT 0
+
+/* SCALER destination width/height */
+#define SCALER_DST_WH 0x40
+#define SCALER_DST_WIDTH_MASK 0x3fff
+#define SCALER_DST_WIDTH_SHIFT 16
+#define SCALER_DST_HEIGHT_MASK 0x3fff
+#define SCALER_DST_HEIGHT_SHIFT 0
+
+/* SCALER destination position */
+#define SCALER_DST_POS 0x44
+#define SCALER_DST_H_POS_MASK 0x3fff
+#define SCALER_DST_H_POS_SHIFT 16
+#define SCALER_DST_V_POS_MASK 0x3fff
+#define SCALER_DST_V_POS_SHIFT 0
+
+/* SCALER horizontal scale ratio */
+#define SCALER_H_RATIO 0x50
+#define SCALER_H_RATIO_MASK 0x7ffff
+#define SCALER_H_RATIO_SHIFT 0
+
+/* SCALER vertical scale ratio */
+#define SCALER_V_RATIO 0x54
+#define SCALER_V_RATIO_MASK 0x7ffff
+#define SCALER_V_RATIO_SHIFT 0
+
+/* SCALER rotation config */
+#define SCALER_ROT_CFG 0x58
+#define SCALER_FLIP_X_EN (1 << 3)
+#define SCALER_FLIP_Y_EN (1 << 2)
+#define SCALER_ROTMODE_MASK 0x3
+#define SCALER_ROTMODE_SHIFT 0
+
+/* SCALER csc coefficients */
+#define SCALER_CSC_COEF(x, y) (0x220 + ((x * 12) + (y * 4)))
+
+/* SCALER dither config */
+#define SCALER_DITH_CFG 0x250
+#define SCALER_DITHER_R_TYPE_MASK 0x7
+#define SCALER_DITHER_R_TYPE_SHIFT 6
+#define SCALER_DITHER_G_TYPE_MASK 0x7
+#define SCALER_DITHER_G_TYPE_SHIFT 3
+#define SCALER_DITHER_B_TYPE_MASK 0x7
+#define SCALER_DITHER_B_TYPE_SHIFT 0
+
+/* SCALER src blend color */
+#define SCALER_SRC_BLEND_COLOR 0x280
+#define SCALER_SRC_COLOR_SEL_INV (1 << 31)
+#define SCALER_SRC_COLOR_SEL_MASK 0x3
+#define SCALER_SRC_COLOR_SEL_SHIFT 29
+#define SCALER_SRC_COLOR_OP_SEL_INV (1 << 28)
+#define SCALER_SRC_COLOR_OP_SEL_MASK 0xf
+#define SCALER_SRC_COLOR_OP_SEL_SHIFT 24
+#define SCALER_SRC_GLOBAL_COLOR0_MASK 0xff
+#define SCALER_SRC_GLOBAL_COLOR0_SHIFT 16
+#define SCALER_SRC_GLOBAL_COLOR1_MASK 0xff
+#define SCALER_SRC_GLOBAL_COLOR1_SHIFT 8
+#define SCALER_SRC_GLOBAL_COLOR2_MASK 0xff
+#define SCALER_SRC_GLOBAL_COLOR2_SHIFT 0
+
+/* SCALER src blend alpha */
+#define SCALER_SRC_BLEND_ALPHA 0x284
+#define SCALER_SRC_ALPHA_SEL_INV (1 << 31)
+#define SCALER_SRC_ALPHA_SEL_MASK 0x3
+#define SCALER_SRC_ALPHA_SEL_SHIFT 29
+#define SCALER_SRC_ALPHA_OP_SEL_INV (1 << 28)
+#define SCALER_SRC_ALPHA_OP_SEL_MASK 0xf
+#define SCALER_SRC_ALPHA_OP_SEL_SHIFT 24
+#define SCALER_SRC_GLOBAL_ALPHA_MASK 0xff
+#define SCALER_SRC_GLOBAL_ALPHA_SHIFT 0
+
+/* SCALER dst blend color */
+#define SCALER_DST_BLEND_COLOR 0x288
+#define SCALER_DST_COLOR_SEL_INV (1 << 31)
+#define SCALER_DST_COLOR_SEL_MASK 0x3
+#define SCALER_DST_COLOR_SEL_SHIFT 29
+#define SCALER_DST_COLOR_OP_SEL_INV (1 << 28)
+#define SCALER_DST_COLOR_OP_SEL_MASK 0xf
+#define SCALER_DST_COLOR_OP_SEL_SHIFT 24
+#define SCALER_DST_GLOBAL_COLOR0_MASK 0xff
+#define SCALER_DST_GLOBAL_COLOR0_SHIFT 16
+#define SCALER_DST_GLOBAL_COLOR1_MASK 0xff
+#define SCALER_DST_GLOBAL_COLOR1_SHIFT 8
+#define SCALER_DST_GLOBAL_COLOR2_MASK 0xff
+#define SCALER_DST_GLOBAL_COLOR2_SHIFT 0
+
+/* SCALER dst blend alpha */
+#define SCALER_DST_BLEND_ALPHA 0x28c
+#define SCALER_DST_ALPHA_SEL_INV (1 << 31)
+#define SCALER_DST_ALPHA_SEL_MASK 0x3
+#define SCALER_DST_ALPHA_SEL_SHIFT 29
+#define SCALER_DST_ALPHA_OP_SEL_INV (1 << 28)
+#define SCALER_DST_ALPHA_OP_SEL_MASK 0xf
+#define SCALER_DST_ALPHA_OP_SEL_SHIFT 24
+#define SCALER_DST_GLOBAL_ALPHA_MASK 0xff
+#define SCALER_DST_GLOBAL_ALPHA_SHIFT 0
+
+/* SCALER fill color */
+#define SCALER_FILL_COLOR 0x290
+#define SCALER_FILL_ALPHA_MASK 0xff
+#define SCALER_FILL_ALPHA_SHIFT 24
+#define SCALER_FILL_COLOR0_MASK 0xff
+#define SCALER_FILL_COLOR0_SHIFT 16
+#define SCALER_FILL_COLOR1_MASK 0xff
+#define SCALER_FILL_COLOR1_SHIFT 8
+#define SCALER_FILL_COLOR2_MASK 0xff
+#define SCALER_FILL_COLOR2_SHIFT 0
+
+/* SCALER address queue config */
+#define SCALER_ADDR_QUEUE_CONFIG 0x2a0
+#define SCALER_ADDR_QUEUE_RST 0x1
+
+/* Arbitrary R/W register and value to check if soft reset succeeded */
+#define SCALER_CFG_SOFT_RESET_CHECK_REG SCALER_SRC_CFG
+#define SCALER_CFG_SOFT_RESET_CHECK_VAL 0x3
+
+struct scaler_error {
+ u32 irq_num;
+ const char * const name;
+};
+
+static const struct scaler_error scaler_errors[] = {
+ { SCALER_INT_TIMEOUT, "Timeout" },
+ { SCALER_INT_ILLEGAL_BLEND, "Blend setting" },
+ { SCALER_INT_ILLEGAL_RATIO, "Scale ratio setting" },
+ { SCALER_INT_ILLEGAL_DST_HEIGHT, "Dst Height" },
+ { SCALER_INT_ILLEGAL_DST_WIDTH, "Dst Width" },
+ { SCALER_INT_ILLEGAL_DST_V_POS, "Dst V-Pos" },
+ { SCALER_INT_ILLEGAL_DST_H_POS, "Dst H-Pos" },
+ { SCALER_INT_ILLEGAL_DST_C_SPAN, "Dst C-Span" },
+ { SCALER_INT_ILLEGAL_DST_Y_SPAN, "Dst Y-span" },
+ { SCALER_INT_ILLEGAL_DST_CR_BASE, "Dst Cr-base" },
+ { SCALER_INT_ILLEGAL_DST_CB_BASE, "Dst Cb-base" },
+ { SCALER_INT_ILLEGAL_DST_Y_BASE, "Dst Y-base" },
+ { SCALER_INT_ILLEGAL_DST_COLOR, "Dst Color" },
+ { SCALER_INT_ILLEGAL_SRC_HEIGHT, "Src Height" },
+ { SCALER_INT_ILLEGAL_SRC_WIDTH, "Src Width" },
+ { SCALER_INT_ILLEGAL_SRC_CV_POS, "Src Chroma V-pos" },
+ { SCALER_INT_ILLEGAL_SRC_CH_POS, "Src Chroma H-pos" },
+ { SCALER_INT_ILLEGAL_SRC_YV_POS, "Src Luma V-pos" },
+ { SCALER_INT_ILLEGAL_SRC_YH_POS, "Src Luma H-pos" },
+ { SCALER_INT_ILLEGAL_SRC_C_SPAN, "Src C-span" },
+ { SCALER_INT_ILLEGAL_SRC_Y_SPAN, "Src Y-span" },
+ { SCALER_INT_ILLEGAL_SRC_CR_BASE, "Src Cr-base" },
+ { SCALER_INT_ILLEGAL_SRC_CB_BASE, "Src Cb-base" },
+ { SCALER_INT_ILLEGAL_SRC_Y_BASE, "Src Y-base" },
+ { SCALER_INT_ILLEGAL_SRC_COLOR, "Src Color setting" },
+};
+
+#define SCALER_NUM_ERRORS ARRAY_SIZE(scaler_errors)
+
+static inline u32 scaler_read(struct scaler_dev *dev, u32 offset)
+{
+ return readl(dev->regs + offset);
+}
+
+static inline void scaler_write(struct scaler_dev *dev, u32 offset, u32 value)
+{
+ writel(value, dev->regs + offset);
+}
+
+static inline void scaler_hw_address_queue_reset(struct scaler_ctx *ctx)
+{
+ scaler_write(ctx->scaler_dev, SCALER_ADDR_QUEUE_CONFIG,
+ SCALER_ADDR_QUEUE_RST);
+}
+
+void scaler_hw_set_sw_reset(struct scaler_dev *dev);
+int scaler_wait_reset(struct scaler_dev *dev);
+void scaler_hw_set_irq(struct scaler_dev *dev, int interrupt, bool mask);
+void scaler_hw_set_input_addr(struct scaler_dev *dev, struct scaler_addr *addr);
+void scaler_hw_set_output_addr(struct scaler_dev *dev,
+ struct scaler_addr *addr);
+void scaler_hw_set_in_size(struct scaler_ctx *ctx);
+void scaler_hw_set_in_image_format(struct scaler_ctx *ctx);
+void scaler_hw_set_out_size(struct scaler_ctx *ctx);
+void scaler_hw_set_out_image_format(struct scaler_ctx *ctx);
+void scaler_hw_set_scaler_ratio(struct scaler_ctx *ctx);
+void scaler_hw_set_rotation(struct scaler_ctx *ctx);
+void scaler_hw_set_csc_coeff(struct scaler_ctx *ctx);
+void scaler_hw_src_y_offset_en(struct scaler_dev *dev, bool on);
+void scaler_hw_dst_y_offset_en(struct scaler_dev *dev, bool on);
+void scaler_hw_enable_control(struct scaler_dev *dev, bool on);
+unsigned int scaler_hw_get_irq_status(struct scaler_dev *dev);
+void scaler_hw_clear_irq(struct scaler_dev *dev, unsigned int irq);
+
+#endif /* REGS_SCALER_H_ */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver
2014-01-09 3:28 [PATCH v5 0/4] Exynos5 Series SCALER Driver Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER Shaik Ameer Basha
@ 2014-01-09 3:28 ` Shaik Ameer Basha
2014-01-09 8:35 ` Bartlomiej Zolnierkiewicz
2014-01-09 3:28 ` [PATCH v5 3/4] [media] exynos-scaler: Add m2m " Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for " Shaik Ameer Basha
3 siblings, 1 reply; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-09 3:28 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the core functionality for the SCALER driver.
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
drivers/media/platform/exynos-scaler/scaler.c | 1231 +++++++++++++++++++++++++
drivers/media/platform/exynos-scaler/scaler.h | 376 ++++++++
2 files changed, 1607 insertions(+)
create mode 100644 drivers/media/platform/exynos-scaler/scaler.c
create mode 100644 drivers/media/platform/exynos-scaler/scaler.h
diff --git a/drivers/media/platform/exynos-scaler/scaler.c b/drivers/media/platform/exynos-scaler/scaler.c
new file mode 100644
index 0000000..db2cad1
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/scaler.c
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (c) 2013 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/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+#include "scaler-regs.h"
+
+#define SCALER_CLOCK_GATE_NAME "scaler"
+
+static const struct scaler_fmt scaler_formats[] = {
+ {
+ .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV12M,
+ .depth = { 8, 4 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CBCR,
+ .num_planes = 2,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV420_2P_Y_UV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+
+ }, {
+ .name = "YUV 4:2:0 contig. 2p, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .depth = { 12 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV420_2P_Y_UV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled",
+ .pixelformat = V4L2_PIX_FMT_NV12MT_16X16,
+ .depth = { 8, 4 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CBCR,
+ .num_planes = 2,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV420_2P_Y_UV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_TILED,
+ }, {
+ .name = "YUV 4:2:2 contig. 2p, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV16,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV422_2P_Y_UV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:4:4 contig. 2p, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV24,
+ .depth = { 24 },
+ .color = SCALER_YUV444,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV444_2P_Y_UV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "RGB565",
+ .pixelformat = V4L2_PIX_FMT_RGB565X,
+ .depth = { 16 },
+ .color = SCALER_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_RGB565,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "XRGB-1555, 16 bpp",
+ .pixelformat = V4L2_PIX_FMT_RGB555,
+ .depth = { 16 },
+ .color = SCALER_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_ARGB1555,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "XRGB-8888, 32 bpp",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .depth = { 32 },
+ .color = SCALER_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_ARGB8888,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .pixelformat = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CRCB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_YUV422_1P_YVYU,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_YUV422_1P_YUYV,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_YUV422_1P_UYVY,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "XRGB-4444, 16 bpp",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .depth = { 16 },
+ .color = SCALER_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .scaler_color = SCALER_ARGB4444,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 2p, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV21M,
+ .depth = { 8, 4 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CRCB,
+ .num_planes = 2,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV420_2P_Y_VU,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 contig. 2p, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .depth = { 12 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CRCB,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV420_2P_Y_VU,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:2 contig. 2p, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV61,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CRCB,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV422_2P_Y_VU,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:4:4 contig. 2p, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV42,
+ .depth = { 24 },
+ .color = SCALER_YUV444,
+ .color_order = SCALER_CRCB,
+ .num_planes = 1,
+ .num_comp = 2,
+ .scaler_color = SCALER_YUV444_2P_Y_VU,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 contig. 3p, YCbCr",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .depth = { 12 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 3,
+ .scaler_color = SCALER_YUV420_3P_Y_U_V,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 contig. 3p, YCrCb",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .depth = { 12 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CRCB,
+ .num_planes = 1,
+ .num_comp = 3,
+ .scaler_color = SCALER_YUV420_3P_Y_U_V,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV420M,
+ .depth = { 8, 2, 2 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CBCR,
+ .num_planes = 3,
+ .num_comp = 3,
+ .scaler_color = SCALER_YUV420_3P_Y_U_V,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 3p, Y/Cr/Cb",
+ .pixelformat = V4L2_PIX_FMT_YVU420M,
+ .depth = { 8, 2, 2 },
+ .color = SCALER_YUV420,
+ .color_order = SCALER_CRCB,
+ .num_planes = 3,
+ .num_comp = 3,
+ .scaler_color = SCALER_YUV420_3P_Y_U_V,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ }, {
+ .name = "YUV 4:2:2 contig. 3p, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .depth = { 16 },
+ .color = SCALER_YUV422,
+ .color_order = SCALER_CBCR,
+ .num_planes = 1,
+ .num_comp = 3,
+ .scaler_color = SCALER_YUV422_3P_Y_U_V,
+ .flags = SCALER_FMT_SRC | SCALER_FMT_DST,
+ },
+
+ /*
+ * TODO: support pixel formats, corresponds to these scaler_color
+ * formats. SCALER_L8A8, SCALER_RGBA8888, SCALER_L8 etc
+ */
+};
+
+const struct scaler_fmt *scaler_get_format(int index)
+{
+ if (index >= ARRAY_SIZE(scaler_formats))
+ return NULL;
+
+ return &scaler_formats[index];
+}
+
+const struct scaler_fmt *scaler_find_fmt(u32 *pixelformat, int index)
+{
+ unsigned int i;
+ int num_fmts = ARRAY_SIZE(scaler_formats);
+
+ if (index >= num_fmts)
+ return NULL;
+
+ if (index >= 0)
+ return &scaler_formats[index];
+
+ if (!pixelformat)
+ return NULL;
+
+ for (i = 0; i < num_fmts; ++i)
+ if (scaler_formats[i].pixelformat == *pixelformat)
+ return &scaler_formats[i];
+
+ return NULL;
+}
+
+void scaler_set_frame_size(struct scaler_frame *frame, int width, int height)
+{
+ frame->f_width = width;
+ frame->f_height = height;
+ frame->selection.width = width;
+ frame->selection.height = height;
+ frame->selection.left = 0;
+ frame->selection.top = 0;
+}
+
+int scaler_enum_fmt_mplane(struct v4l2_fmtdesc *f)
+{
+ const struct scaler_fmt *fmt;
+
+ fmt = scaler_find_fmt(NULL, f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ /*
+ * Input supports all scaler_formats but all scaler_formats are not
+ * supported for output. Don't return unsupported formats for output.
+ */
+ if (!(V4L2_TYPE_IS_OUTPUT(f->type) &&
+ (fmt->flags & SCALER_FMT_SRC)))
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->pixelformat;
+
+ return 0;
+}
+
+struct scaler_frame *ctx_get_frame(struct scaler_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return &ctx->s_frame;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return &ctx->d_frame;
+
+ scaler_dbg(ctx->scaler_dev, "Wrong buffer/video queue type (%d)", type);
+ return ERR_PTR(-EINVAL);
+}
+
+
+static u32 get_plane_info(struct scaler_frame *frm, u32 addr, u32 *index)
+{
+ if (frm->addr.y == addr) {
+ *index = 0;
+ return frm->addr.y;
+ } else if (frm->addr.cb == addr) {
+ *index = 1;
+ return frm->addr.cb;
+ } else if (frm->addr.cr == addr) {
+ *index = 2;
+ return frm->addr.cr;
+ }
+
+ pr_debug("Plane address is wrong\n");
+ return -EINVAL;
+}
+
+void scaler_set_prefbuf(struct scaler_dev *scaler, struct scaler_frame *frm)
+{
+ u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
+ f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
+
+ f_chk_addr = frm->addr.y;
+ f_chk_len = frm->payload[0];
+
+ if (frm->fmt->num_planes == 2) {
+ s_chk_addr = frm->addr.cb;
+ s_chk_len = frm->payload[1];
+ } else if (frm->fmt->num_planes == 3) {
+ u32 low_addr, low_plane, mid_addr, mid_plane;
+ u32 high_addr, high_plane;
+ u32 t_min, t_max;
+
+ t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr);
+ low_addr = get_plane_info(frm, t_min, &low_plane);
+ t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr);
+ high_addr = get_plane_info(frm, t_max, &high_plane);
+
+ mid_plane = 3 - (low_plane + high_plane);
+ if (mid_plane == 0)
+ mid_addr = frm->addr.y;
+ else if (mid_plane == 1)
+ mid_addr = frm->addr.cb;
+ else if (mid_plane == 2)
+ mid_addr = frm->addr.cr;
+ else
+ return;
+
+ f_chk_addr = low_addr;
+ if (mid_addr + frm->payload[mid_plane] - low_addr >
+ high_addr + frm->payload[high_plane] - mid_addr) {
+ f_chk_len = frm->payload[low_plane];
+ s_chk_addr = mid_addr;
+ s_chk_len = high_addr +
+ frm->payload[high_plane] - mid_addr;
+ } else {
+ f_chk_len = mid_addr +
+ frm->payload[mid_plane] - low_addr;
+ s_chk_addr = high_addr;
+ s_chk_len = frm->payload[high_plane];
+ }
+ }
+
+ scaler_dbg(scaler,
+ "f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
+ f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
+}
+
+int scaler_try_fmt_mplane(struct scaler_ctx *ctx, struct v4l2_format *f)
+{
+ struct scaler_dev *scaler = ctx->scaler_dev;
+ struct device *dev = &scaler->pdev->dev;
+ struct scaler_variant *variant = scaler->variant;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ const struct scaler_fmt *fmt;
+ u32 max_w, max_h, mod_w = 0, mod_h = 0;
+ u32 min_w, min_h, tmp_w, tmp_h;
+ int i;
+ struct scaler_frm_limit *frm_limit;
+
+ scaler_dbg(scaler, "user put w: %d, h: %d",
+ pix_mp->width, pix_mp->height);
+
+ fmt = scaler_find_fmt(&pix_mp->pixelformat, -1);
+ if (!fmt) {
+ scaler_dbg(scaler, "pixelformat format (0x%X) invalid\n",
+ pix_mp->pixelformat);
+ /* Falling back to default pixel format */
+ fmt = scaler_find_fmt(NULL, 0);
+ pix_mp->pixelformat = fmt->pixelformat;
+ }
+
+ pix_mp->field = V4L2_FIELD_NONE;
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ frm_limit = variant->pix_out;
+ else
+ frm_limit = variant->pix_in;
+
+ max_w = frm_limit->max_w;
+ max_h = frm_limit->max_h;
+ min_w = frm_limit->min_w;
+ min_h = frm_limit->min_h;
+
+ /* Span has to be even number for YCbCr422-2p or YCbCr420 format. */
+ if (is_yuv422_2p(fmt) || is_yuv420(fmt))
+ mod_w = 1;
+
+ scaler_dbg(scaler, "mod_w: %d, mod_h: %d, max_w: %d, max_h = %d",
+ mod_w, mod_h, max_w, max_h);
+
+ tmp_w = pix_mp->width;
+ tmp_h = pix_mp->height;
+
+ v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_w,
+ &pix_mp->height, min_h, max_h, mod_h, 0);
+ if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
+ dev_info(dev,
+ "Image size has been modified from %dx%d to %dx%d",
+ tmp_w, tmp_h, pix_mp->width, pix_mp->height);
+
+ pix_mp->num_planes = fmt->num_planes;
+
+ /*
+ * Nothing mentioned about the colorspace in SCALER. Default value is
+ * set to V4L2_COLORSPACE_REC709.
+ */
+ pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+
+ for (i = 0; i < pix_mp->num_planes; ++i) {
+ int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
+ pix_mp->plane_fmt[i].bytesperline = bpl;
+ pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
+
+ scaler_dbg(scaler, "[%d]: bpl: %d, sizeimage: %d",
+ i, bpl, pix_mp->plane_fmt[i].sizeimage);
+ }
+
+ return 0;
+}
+
+int scaler_g_fmt_mplane(struct scaler_ctx *ctx, struct v4l2_format *f)
+{
+ struct scaler_frame *frame;
+ struct v4l2_pix_format_mplane *pix_mp;
+ int i;
+
+ frame = ctx_get_frame(ctx, f->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ pix_mp = &f->fmt.pix_mp;
+
+ pix_mp->width = frame->f_width;
+ pix_mp->height = frame->f_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->pixelformat = frame->fmt->pixelformat;
+ pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+ pix_mp->num_planes = frame->fmt->num_planes;
+
+ for (i = 0; i < pix_mp->num_planes; ++i) {
+ pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
+ frame->fmt->depth[i]) / 8;
+ pix_mp->plane_fmt[i].sizeimage =
+ pix_mp->plane_fmt[i].bytesperline * frame->f_height;
+ }
+
+ return 0;
+}
+
+void scaler_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
+{
+ if (tmp_w != *w || tmp_h != *h) {
+ pr_info("Cropped size has been modified from %dx%d to %dx%d",
+ *w, *h, tmp_w, tmp_h);
+ *w = tmp_w;
+ *h = tmp_h;
+ }
+}
+
+int scaler_g_crop(struct scaler_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct scaler_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c = frame->selection;
+
+ return 0;
+}
+
+int scaler_try_crop(struct scaler_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct scaler_frame *f;
+ const struct scaler_fmt *fmt;
+ struct scaler_dev *scaler = ctx->scaler_dev;
+ struct scaler_variant *variant = scaler->variant;
+ u32 mod_w = 0, mod_h = 0, tmp_w, tmp_h;
+ u32 min_w, min_h, max_w, max_h;
+ struct scaler_frm_limit *frm_limit;
+
+ if (cr->c.top < 0) {
+ scaler_dbg(scaler, "Adjusting crop.top value\n");
+ cr->c.top = 0;
+ }
+
+ if (cr->c.left < 0) {
+ scaler_dbg(scaler, "Adjusting crop.left value\n");
+ cr->c.left = 0;
+ }
+
+ scaler_dbg(scaler, "user requested width: %d, height: %d",
+ cr->c.width, cr->c.height);
+
+ f = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ fmt = f->fmt;
+ tmp_w = cr->c.width;
+ tmp_h = cr->c.height;
+
+ if (V4L2_TYPE_IS_OUTPUT(cr->type))
+ frm_limit = variant->pix_out;
+ else
+ frm_limit = variant->pix_in;
+
+ max_w = f->f_width;
+ max_h = f->f_height;
+ min_w = frm_limit->min_w;
+ min_h = frm_limit->min_h;
+
+ if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
+ if (is_yuv420(fmt)) {
+ mod_w = ffs(variant->pix_align->dst_w_420) - 1;
+ mod_h = ffs(variant->pix_align->dst_h_420) - 1;
+ } else if (is_yuv422(fmt)) {
+ mod_w = ffs(variant->pix_align->dst_w_422) - 1;
+ }
+ } else {
+ if (is_yuv420(fmt)) {
+ mod_w = ffs(variant->pix_align->src_w_420) - 1;
+ mod_h = ffs(variant->pix_align->src_h_420) - 1;
+ } else if (is_yuv422(fmt)) {
+ mod_w = ffs(variant->pix_align->src_w_422) - 1;
+ }
+
+ if (ctx->ctrls_scaler.rotate->val == 90 ||
+ ctx->ctrls_scaler.rotate->val == 270) {
+ max_w = f->f_height;
+ max_h = f->f_width;
+ tmp_w = cr->c.height;
+ tmp_h = cr->c.width;
+ }
+ }
+
+ scaler_dbg(scaler, "mod_x: %d, mod_y: %d, min_w: %d, min_h = %d, tmp_w : %d, tmp_h : %d",
+ mod_w, mod_h, min_w, min_h, tmp_w, tmp_h);
+
+ v4l_bound_align_image(&tmp_w, min_w, max_w, mod_w,
+ &tmp_h, min_h, max_h, mod_h, 0);
+
+ if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
+ (ctx->ctrls_scaler.rotate->val == 90 ||
+ ctx->ctrls_scaler.rotate->val == 270))
+ scaler_check_crop_change(tmp_h, tmp_w,
+ &cr->c.width, &cr->c.height);
+ else
+ scaler_check_crop_change(tmp_w, tmp_h,
+ &cr->c.width, &cr->c.height);
+
+ /*
+ * Adjust left/top if cropping rectangle is out of bounds. Need to add
+ * code to algin left value with 2's multiple.
+ */
+ if (cr->c.left + tmp_w > max_w)
+ cr->c.left = max_w - tmp_w;
+ if (cr->c.top + tmp_h > max_h)
+ cr->c.top = max_h - tmp_h;
+
+ if (is_yuv422_1p(fmt) && (cr->c.left & 1))
+ cr->c.left -= 1;
+
+ scaler_dbg(scaler, "Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+ cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
+
+ return 0;
+}
+
+int scaler_check_scaler_ratio(struct scaler_variant *var, int sw, int sh,
+ int dw, int dh, int rot)
+{
+ if ((dw == 0) || (dh == 0))
+ return -EINVAL;
+
+ if (rot == 90 || rot == 270)
+ swap(dh, dw);
+
+ pr_debug("sw: %d, sh: %d, dw: %d, dh: %d\n", sw, sh, dw, dh);
+
+ if ((sw / dw) > var->scl_down_max || (sh / dh) > var->scl_down_max ||
+ (dw / sw) > var->scl_up_max || (dh / sh) > var->scl_up_max)
+ return -EINVAL;
+
+ return 0;
+}
+
+int scaler_set_scaler_info(struct scaler_ctx *ctx)
+{
+ struct scaler_scaler *sc = &ctx->scaler;
+ struct scaler_frame *s_frame = &ctx->s_frame;
+ struct scaler_frame *d_frame = &ctx->d_frame;
+ struct scaler_variant *variant = ctx->scaler_dev->variant;
+ int src_w, src_h, ret;
+
+ ret = scaler_check_scaler_ratio(variant,
+ s_frame->selection.width, s_frame->selection.height,
+ d_frame->selection.width, d_frame->selection.height,
+ ctx->ctrls_scaler.rotate->val);
+ if (ret < 0) {
+ scaler_dbg(ctx->scaler_dev, "out of scaler range\n");
+ return ret;
+ }
+
+ if (ctx->ctrls_scaler.rotate->val == 90 ||
+ ctx->ctrls_scaler.rotate->val == 270) {
+ src_w = s_frame->selection.height;
+ src_h = s_frame->selection.width;
+ } else {
+ src_w = s_frame->selection.width;
+ src_h = s_frame->selection.height;
+ }
+
+ sc->hratio = (src_w << 16) / d_frame->selection.width;
+ sc->vratio = (src_h << 16) / d_frame->selection.height;
+
+ scaler_dbg(ctx->scaler_dev, "scaler settings::\n"
+ "sx = %d, sy = %d, sw = %d, sh = %d\n"
+ "dx = %d, dy = %d, dw = %d, dh = %d\n"
+ "h-ratio : %d, v-ratio: %d\n",
+ s_frame->selection.left, s_frame->selection.top,
+ s_frame->selection.width, s_frame->selection.height,
+ d_frame->selection.left, d_frame->selection.top,
+ d_frame->selection.width, s_frame->selection.height,
+ sc->hratio, sc->vratio);
+
+ return 0;
+}
+
+static int __scaler_try_ctrl(struct scaler_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ struct scaler_dev *scaler = ctx->scaler_dev;
+ struct scaler_variant *variant = scaler->variant;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ROTATE:
+ return scaler_check_scaler_ratio(variant,
+ ctx->s_frame.selection.width,
+ ctx->s_frame.selection.height,
+ ctx->d_frame.selection.width,
+ ctx->d_frame.selection.height,
+ ctx->ctrls_scaler.rotate->val);
+ }
+
+ return 0;
+}
+
+static int scaler_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct scaler_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->scaler_dev->slock, flags);
+ ret = __scaler_try_ctrl(ctx, ctrl);
+ spin_unlock_irqrestore(&ctx->scaler_dev->slock, flags);
+
+ return ret;
+}
+
+static int __scaler_s_ctrl(struct scaler_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctx->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ ctx->vflip = ctrl->val;
+ break;
+
+ case V4L2_CID_ROTATE:
+ ctx->rotation = ctrl->val;
+ break;
+
+ case V4L2_CID_ALPHA_COMPONENT:
+ ctx->d_frame.alpha = ctrl->val;
+ break;
+ }
+
+ ctx->state |= SCALER_PARAMS;
+ return 0;
+}
+
+static int scaler_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct scaler_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->scaler_dev->slock, flags);
+ ret = __scaler_s_ctrl(ctx, ctrl);
+ spin_unlock_irqrestore(&ctx->scaler_dev->slock, flags);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops scaler_ctrl_ops = {
+ .try_ctrl = scaler_try_ctrl,
+ .s_ctrl = scaler_s_ctrl,
+};
+
+int scaler_ctrls_create(struct scaler_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ scaler_dbg(ctx->scaler_dev,
+ "Control handler of this ctx was created already");
+ return 0;
+ }
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, SCALER_MAX_CTRL_NUM);
+
+ ctx->ctrls_scaler.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &scaler_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
+ ctx->ctrls_scaler.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &scaler_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctx->ctrls_scaler.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &scaler_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ctx->ctrls_scaler.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &scaler_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
+
+ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ dev_err(&ctx->scaler_dev->pdev->dev,
+ "Failed to create SCALER control handlers");
+ return err;
+ }
+
+ return 0;
+}
+
+void scaler_ctrls_delete(struct scaler_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ ctx->ctrls_rdy = false;
+ }
+}
+
+/* The color format (num_comp, num_planes) must be already configured. */
+int scaler_prepare_addr(struct scaler_ctx *ctx, struct vb2_buffer *vb,
+ struct scaler_frame *frame, struct scaler_addr *addr)
+{
+ int ret = 0;
+ u32 pix_size;
+ const struct scaler_fmt *fmt;
+
+ if (vb == NULL || frame == NULL)
+ return -EINVAL;
+
+ pix_size = frame->f_width * frame->f_height;
+ fmt = frame->fmt;
+
+ scaler_dbg(ctx->scaler_dev,
+ "planes= %d, comp= %d, pix_size= %d, fmt = %d\n",
+ fmt->num_planes, fmt->num_comp,
+ pix_size, fmt->scaler_color);
+
+ addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (fmt->num_planes == 1) {
+ switch (fmt->num_comp) {
+ case 1:
+ addr->cb = 0;
+ addr->cr = 0;
+ break;
+ case 2:
+ /* Decompose Y into Y/Cb */
+ addr->cb = (dma_addr_t)(addr->y + pix_size);
+ addr->cr = 0;
+ break;
+ case 3:
+ /* Decompose Y into Y/Cb/Cr */
+ addr->cb = (dma_addr_t)(addr->y + pix_size);
+ if (SCALER_YUV420 == fmt->color)
+ addr->cr = (dma_addr_t)(addr->cb
+ + (pix_size >> 2));
+ else if (SCALER_YUV422 == fmt->color)
+ addr->cr = (dma_addr_t)(addr->cb
+ + (pix_size >> 1));
+ else /* 444 */
+ addr->cr = (dma_addr_t)(addr->cb + pix_size);
+ break;
+ default:
+ scaler_dbg(ctx->scaler_dev,
+ "Invalid number of color planes\n");
+ return -EINVAL;
+ }
+ } else {
+ if (fmt->num_planes >= 2)
+ addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+
+ if (fmt->num_planes == 3)
+ addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+ }
+
+ if ((fmt->color_order == SCALER_CRCB) && (fmt->num_planes == 3))
+ swap(addr->cb, addr->cr);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+ scaler_dbg(ctx->scaler_dev,
+ "\nIN:ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d\n",
+ addr->y, addr->cb, addr->cr, ret);
+ else
+ scaler_dbg(ctx->scaler_dev,
+ "\nOUT:ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d\n",
+ addr->y, addr->cb, addr->cr, ret);
+
+ return ret;
+}
+
+static void scaler_sw_reset(struct scaler_dev *scaler)
+{
+ scaler_hw_set_sw_reset(scaler);
+ scaler_wait_reset(scaler);
+
+ scaler->coeff_type = SCALER_CSC_COEFF_NONE;
+}
+
+static void scaler_check_for_illegal_status(struct device *dev,
+ unsigned int irq_status)
+{
+ int i;
+
+ for (i = 0; i < SCALER_NUM_ERRORS; i++)
+ if ((1 << scaler_errors[i].irq_num) & irq_status)
+ dev_err(dev, "ERROR:: %s\n", scaler_errors[i].name);
+}
+
+static irqreturn_t scaler_irq_handler(int irq, void *priv)
+{
+ struct scaler_dev *scaler = priv;
+ struct scaler_ctx *ctx;
+ unsigned int scaler_irq;
+ struct device *dev = &scaler->pdev->dev;
+
+ scaler_irq = scaler_hw_get_irq_status(scaler);
+ scaler_dbg(scaler, "irq_status: 0x%x\n", scaler_irq);
+ scaler_hw_clear_irq(scaler, scaler_irq);
+
+ if (scaler_irq & SCALER_INT_STATUS_ERROR)
+ scaler_check_for_illegal_status(dev, scaler_irq);
+
+ if (!(scaler_irq & (1 << SCALER_INT_FRAME_END)))
+ return IRQ_HANDLED;
+
+ spin_lock(&scaler->slock);
+
+ if (test_and_clear_bit(ST_M2M_PEND, &scaler->state)) {
+
+ scaler_hw_enable_control(scaler, false);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDING, &scaler->state)) {
+ set_bit(ST_M2M_SUSPENDED, &scaler->state);
+ wake_up(&scaler->irq_queue);
+ goto isr_unlock;
+ }
+ ctx = v4l2_m2m_get_curr_priv(scaler->m2m.m2m_dev);
+
+ if (!ctx || !ctx->m2m_ctx)
+ goto isr_unlock;
+
+ spin_unlock(&scaler->slock);
+ scaler_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
+
+ /* wake_up job_abort, stop_streaming */
+ if (ctx->state & SCALER_CTX_STOP_REQ) {
+ ctx->state &= ~SCALER_CTX_STOP_REQ;
+ wake_up(&scaler->irq_queue);
+ }
+ return IRQ_HANDLED;
+ }
+
+isr_unlock:
+ spin_unlock(&scaler->slock);
+ return IRQ_HANDLED;
+}
+
+static struct scaler_frm_limit scaler_frm_limit_5410 = {
+ .min_w = 4,
+ .min_h = 4,
+ .max_w = 4096,
+ .max_h = 4096,
+};
+
+static struct scaler_frm_limit scaler_inp_frm_limit_5420 = {
+ .min_w = 16,
+ .min_h = 16,
+ .max_w = 8192,
+ .max_h = 8192,
+};
+
+static struct scaler_frm_limit scaler_out_frm_limit_5420 = {
+ .min_w = 4,
+ .min_h = 4,
+ .max_w = 8192,
+ .max_h = 8192,
+};
+
+static struct scaler_pix_align scaler_align_info = {
+ .src_w_420 = 2,
+ .src_w_422 = 2,
+ .src_h_420 = 2,
+ .dst_w_420 = 2,
+ .dst_w_422 = 2,
+ .dst_h_420 = 2,
+};
+
+
+static struct scaler_variant scaler_variant_info_5410 = {
+ .pix_in = &scaler_frm_limit_5410,
+ .pix_out = &scaler_frm_limit_5410,
+ .pix_align = &scaler_align_info,
+ .scl_up_max = 16,
+ .scl_down_max = 4,
+ .in_buf_cnt = 32,
+ .out_buf_cnt = 32,
+};
+
+static struct scaler_variant scaler_variant_info_5420 = {
+ .pix_in = &scaler_inp_frm_limit_5420,
+ .pix_out = &scaler_out_frm_limit_5420,
+ .pix_align = &scaler_align_info,
+ .scl_up_max = 16,
+ .scl_down_max = 4,
+ .in_buf_cnt = 32,
+ .out_buf_cnt = 32,
+};
+
+static const struct of_device_id exynos_scaler_match[] = {
+ {
+ .compatible = "samsung,exynos5410-scaler",
+ .data = &scaler_variant_info_5410,
+ },
+ {
+ .compatible = "samsung,exynos5420-scaler",
+ .data = &scaler_variant_info_5420,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_scaler_match);
+
+static void *scaler_get_variant_data(struct platform_device *pdev)
+{
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(exynos_scaler_match, pdev->dev.of_node);
+ if (match)
+ return (void *)match->data;
+ }
+
+ return NULL;
+}
+
+static void scaler_clk_put(struct scaler_dev *scaler)
+{
+ if (!IS_ERR(scaler->clock))
+ clk_unprepare(scaler->clock);
+}
+
+static int scaler_clk_get(struct scaler_dev *scaler)
+{
+ int ret;
+
+ scaler_dbg(scaler, "scaler_clk_get Called\n");
+
+ scaler->clock = devm_clk_get(&scaler->pdev->dev,
+ SCALER_CLOCK_GATE_NAME);
+ if (IS_ERR(scaler->clock)) {
+ dev_err(&scaler->pdev->dev, "failed to get clock: %s\n",
+ SCALER_CLOCK_GATE_NAME);
+ return PTR_ERR(scaler->clock);
+ }
+
+ ret = clk_prepare(scaler->clock);
+ if (ret < 0) {
+ dev_err(&scaler->pdev->dev,
+ "clock prepare fail for clock: %s\n",
+ SCALER_CLOCK_GATE_NAME);
+ scaler->clock = ERR_PTR(-EINVAL);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int scaler_m2m_suspend(struct scaler_dev *scaler)
+{
+ unsigned long flags;
+ int timeout;
+
+ spin_lock_irqsave(&scaler->slock, flags);
+ if (!scaler_m2m_pending(scaler)) {
+ spin_unlock_irqrestore(&scaler->slock, flags);
+ return 0;
+ }
+ clear_bit(ST_M2M_SUSPENDED, &scaler->state);
+ set_bit(ST_M2M_SUSPENDING, &scaler->state);
+ spin_unlock_irqrestore(&scaler->slock, flags);
+
+ timeout = wait_event_timeout(scaler->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &scaler->state),
+ SCALER_SHUTDOWN_TIMEOUT);
+
+ clear_bit(ST_M2M_SUSPENDING, &scaler->state);
+ return timeout == 0 ? -EAGAIN : 0;
+}
+
+static int scaler_m2m_resume(struct scaler_dev *scaler)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scaler->slock, flags);
+ /* Clear for full H/W setup in first run after resume */
+ scaler->m2m.ctx = NULL;
+ spin_unlock_irqrestore(&scaler->slock, flags);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDED, &scaler->state))
+ scaler_m2m_job_finish(scaler->m2m.ctx,
+ VB2_BUF_STATE_ERROR);
+ return 0;
+}
+
+static int scaler_probe(struct platform_device *pdev)
+{
+ struct scaler_dev *scaler;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ scaler = devm_kzalloc(dev, sizeof(*scaler), GFP_KERNEL);
+ if (!scaler)
+ return -ENOMEM;
+
+ scaler->pdev = pdev;
+ scaler->variant = scaler_get_variant_data(pdev);
+
+ init_waitqueue_head(&scaler->irq_queue);
+ spin_lock_init(&scaler->slock);
+ mutex_init(&scaler->lock);
+ scaler->clock = ERR_PTR(-EINVAL);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ scaler->regs = devm_request_and_ioremap(dev, res);
+ if (!scaler->regs)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = scaler_clk_get(scaler);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(dev, res->start, scaler_irq_handler,
+ 0, pdev->name, scaler);
+ if (ret < 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, scaler);
+ pm_runtime_enable(dev);
+
+ /* Initialize the continious memory allocator */
+ scaler->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(scaler->alloc_ctx)) {
+ ret = PTR_ERR(scaler->alloc_ctx);
+ goto err_clk;
+ }
+
+ ret = v4l2_device_register(dev, &scaler->v4l2_dev);
+ if (ret < 0)
+ goto err_clk;
+
+ ret = scaler_register_m2m_device(scaler);
+ if (ret < 0)
+ goto err_v4l2;
+
+ dev_info(dev, "registered successfully\n");
+ return 0;
+
+err_v4l2:
+ v4l2_device_unregister(&scaler->v4l2_dev);
+err_clk:
+ scaler_clk_put(scaler);
+ return ret;
+}
+
+static int scaler_remove(struct platform_device *pdev)
+{
+ struct scaler_dev *scaler = platform_get_drvdata(pdev);
+
+ scaler_unregister_m2m_device(scaler);
+ v4l2_device_unregister(&scaler->v4l2_dev);
+
+ vb2_dma_contig_cleanup_ctx(scaler->alloc_ctx);
+ pm_runtime_disable(&pdev->dev);
+ scaler_clk_put(scaler);
+
+ scaler_dbg(scaler, "%s driver unloaded\n", pdev->name);
+ return 0;
+}
+
+static int scaler_runtime_resume(struct device *dev)
+{
+ struct scaler_dev *scaler = dev_get_drvdata(dev);
+ int ret;
+
+ scaler_dbg(scaler, "state: 0x%lx", scaler->state);
+
+ ret = clk_enable(scaler->clock);
+ if (ret < 0)
+ return ret;
+
+ scaler_sw_reset(scaler);
+
+ return scaler_m2m_resume(scaler);
+}
+
+static int scaler_runtime_suspend(struct device *dev)
+{
+ struct scaler_dev *scaler = dev_get_drvdata(dev);
+ int ret;
+
+ ret = scaler_m2m_suspend(scaler);
+ if (!ret)
+ clk_disable(scaler->clock);
+
+ scaler_dbg(scaler, "state: 0x%lx", scaler->state);
+ return ret;
+}
+
+static int scaler_resume(struct device *dev)
+{
+ struct scaler_dev *scaler = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ scaler_dbg(scaler, "state: 0x%lx", scaler->state);
+
+ /* Do not resume if the device was idle before system suspend */
+ spin_lock_irqsave(&scaler->slock, flags);
+ if (!test_and_clear_bit(ST_SUSPEND, &scaler->state) ||
+ !scaler_m2m_active(scaler)) {
+ spin_unlock_irqrestore(&scaler->slock, flags);
+ return 0;
+ }
+
+ scaler_sw_reset(scaler);
+ spin_unlock_irqrestore(&scaler->slock, flags);
+
+ return scaler_m2m_resume(scaler);
+}
+
+static int scaler_suspend(struct device *dev)
+{
+ struct scaler_dev *scaler = dev_get_drvdata(dev);
+
+ scaler_dbg(scaler, "state: 0x%lx", scaler->state);
+
+ if (test_and_set_bit(ST_SUSPEND, &scaler->state))
+ return 0;
+
+ return scaler_m2m_suspend(scaler);
+}
+
+static const struct dev_pm_ops scaler_pm_ops = {
+ .suspend = scaler_suspend,
+ .resume = scaler_resume,
+ .runtime_suspend = scaler_runtime_suspend,
+ .runtime_resume = scaler_runtime_resume,
+};
+
+static struct platform_driver scaler_driver = {
+ .probe = scaler_probe,
+ .remove = scaler_remove,
+ .driver = {
+ .name = SCALER_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &scaler_pm_ops,
+ .of_match_table = exynos_scaler_match,
+ }
+};
+
+module_platform_driver(scaler_driver);
+
+MODULE_AUTHOR("Shaik Ameer Basha <shaik.ameer@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series SCALER driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/exynos-scaler/scaler.h b/drivers/media/platform/exynos-scaler/scaler.h
new file mode 100644
index 0000000..9bb7f99
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/scaler.h
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for 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.
+ */
+
+#ifndef SCALER_CORE_H_
+#define SCALER_CORE_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define scaler_dbg(_dev, fmt, args...) dev_dbg(&_dev->pdev->dev, fmt, ##args)
+
+#define SCALER_MODULE_NAME "exynos5-scaler"
+
+#define SCALER_SHUTDOWN_TIMEOUT ((100 * HZ) / 1000)
+#define SCALER_MAX_DEVS 4
+#define SCALER_MAX_CTRL_NUM 10
+#define SCALER_SC_ALIGN_4 4
+#define SCALER_SC_ALIGN_2 2
+#define DEFAULT_CSC_EQ 1
+#define DEFAULT_CSC_RANGE 1
+#define SCALER_MAX_PLANES 3
+
+#define SCALER_PARAMS (1 << 0)
+#define SCALER_CTX_STOP_REQ (1 << 1)
+
+/* color format */
+#define SCALER_RGB (0x1 << 0)
+#define SCALER_YUV420 (0x1 << 1)
+#define SCALER_YUV422 (0x1 << 2)
+#define SCALER_YUV444 (0x1 << 3)
+
+/* yuv color order */
+#define SCALER_CBCR 0
+#define SCALER_CRCB 1
+
+#define SCALER_YUV420_2P_Y_UV 0
+#define SCALER_YUV422_2P_Y_UV 2
+#define SCALER_YUV444_2P_Y_UV 3
+#define SCALER_RGB565 4
+#define SCALER_ARGB1555 5
+#define SCALER_ARGB8888 6
+#define SCALER_PREMULTIPLIED_ARGB8888 7
+#define SCALER_YUV422_1P_YVYU 9
+#define SCALER_YUV422_1P_YUYV 10
+#define SCALER_YUV422_1P_UYVY 11
+#define SCALER_ARGB4444 12
+#define SCALER_L8A8 13
+#define SCALER_RGBA8888 14
+#define SCALER_L8 15
+#define SCALER_YUV420_2P_Y_VU 16
+#define SCALER_YUV422_2P_Y_VU 18
+#define SCALER_YUV444_2P_Y_VU 19
+#define SCALER_YUV420_3P_Y_U_V 20
+#define SCALER_YUV422_3P_Y_U_V 22
+#define SCALER_YUV444_3P_Y_U_V 23
+
+#define SCALER_FMT_SRC (0x1 << 0)
+#define SCALER_FMT_DST (0x1 << 1)
+#define SCALER_FMT_TILED (0x1 << 2)
+
+enum scaler_dev_flags {
+ /* for global */
+ ST_SUSPEND,
+
+ /* for m2m node */
+ ST_M2M_OPEN,
+ ST_M2M_RUN,
+ ST_M2M_PEND,
+ ST_M2M_SUSPENDED,
+ ST_M2M_SUSPENDING,
+};
+
+#define fh_to_ctx(__fh) container_of(__fh, struct scaler_ctx, fh)
+#define is_rgb(fmt) (!!(((fmt)->color) & SCALER_RGB))
+#define is_yuv(fmt) ((fmt->color >= SCALER_YUV420) && \
+ (fmt->color <= SCALER_YUV444))
+#define is_yuv420(fmt) (!!((fmt->color) & SCALER_YUV420))
+#define is_yuv422(fmt) (!!((fmt->color) & SCALER_YUV422))
+#define is_yuv422_1p(fmt) (is_yuv422(fmt) && (fmt->num_planes == 1))
+#define is_yuv420_2p(fmt) (is_yuv420(fmt) && (fmt->num_planes == 2))
+#define is_yuv422_2p(fmt) (is_yuv422(fmt) && (fmt->num_planes == 2))
+#define is_yuv42x_2p(fmt) (is_yuv420_2p(fmt) || is_yuv422_2p(fmt))
+#define is_src_fmt(fmt) ((fmt->flags) & SCALER_FMT_SRC)
+#define is_dst_fmt(fmt) ((fmt->flags) & SCALER_FMT_DST)
+#define is_tiled_fmt(fmt) ((fmt->flags) & SCALER_FMT_TILED)
+
+#define scaler_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
+#define scaler_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+#define scaler_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
+
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct scaler_ctx, ctrl_handler)
+/**
+ * struct scaler_fmt - the driver's internal color format data
+ * @scaler_color: SCALER color format
+ * @name: format description
+ * @pixelformat: the fourcc code for this format, 0 if not applicable
+ * @color_order: Chrominance order control
+ * @num_planes: number of physically non-contiguous data planes
+ * @num_comp: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @flags: flags indicating which operation mode format applies to
+ */
+struct scaler_fmt {
+ u32 scaler_color;
+ char *name;
+ u32 pixelformat;
+ u32 color;
+ u32 color_order;
+ u16 num_planes;
+ u16 num_comp;
+ u8 depth[SCALER_MAX_PLANES];
+ u32 flags;
+};
+
+/**
+ * struct scaler_input_buf - the driver's video buffer
+ * @vb: videobuf2 buffer
+ * @list : linked list structure for buffer queue
+ * @idx : index of SCALER input buffer
+ */
+struct scaler_input_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+ int idx;
+};
+
+/**
+ * struct scaler_addr - the SCALER DMA address set
+ * @y: luminance plane address
+ * @cb: Cb plane address
+ * @cr: Cr plane address
+ */
+struct scaler_addr {
+ dma_addr_t y;
+ dma_addr_t cb;
+ dma_addr_t cr;
+};
+
+/**
+ * struct scaler_ctrls - the SCALER control set
+ * @rotate: rotation degree
+ * @hflip: horizontal flip
+ * @vflip: vertical flip
+ * @global_alpha: the alpha value of current frame
+ */
+struct scaler_ctrls {
+ struct v4l2_ctrl *rotate;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *global_alpha;
+};
+
+/* struct scaler_csc_info - color space conversion information */
+enum scaler_csc_coeff {
+ SCALER_CSC_COEFF_YCBCR_TO_RGB,
+ SCALER_CSC_COEFF_RGB_TO_YCBCR,
+ SCALER_CSC_COEFF_MAX,
+ SCALER_CSC_COEFF_NONE,
+};
+
+struct scaler_csc_info {
+ enum scaler_csc_coeff coeff_type;
+};
+
+/**
+ * struct scaler_scaler - the configuration data for SCALER inetrnal scaler
+ * @hratio: the main scaler's horizontal ratio
+ * @vratio: the main scaler's vertical ratio
+ */
+struct scaler_scaler {
+ u32 hratio;
+ u32 vratio;
+};
+
+struct scaler_dev;
+struct scaler_ctx;
+
+/**
+ * struct scaler_frame - source/target frame properties
+ * @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
+ * @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
+ * @selection: crop(source)/compose(destination) size
+ * @payload: image size in bytes (w x h x bpp)
+ * @addr: image frame buffer DMA addresses
+ * @fmt: SCALER color format pointer
+ * @colorspace: value indicating v4l2_colorspace
+ * @alpha: frame's alpha value
+ */
+struct scaler_frame {
+ u32 f_width;
+ u32 f_height;
+ struct v4l2_rect selection;
+ unsigned long payload[SCALER_MAX_PLANES];
+ struct scaler_addr addr;
+ const struct scaler_fmt *fmt;
+ u32 colorspace;
+ u8 alpha;
+};
+
+/**
+ * struct scaler_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ */
+struct scaler_m2m_device {
+ struct video_device *vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct scaler_ctx *ctx;
+};
+
+/* struct scaler_pix_input - image pixel size limits for input frame */
+struct scaler_frm_limit {
+ u16 min_w;
+ u16 min_h;
+ u16 max_w;
+ u16 max_h;
+
+};
+
+struct scaler_pix_align {
+ u16 src_w_420;
+ u16 src_w_422;
+ u16 src_h_420;
+ u16 dst_w_420;
+ u16 dst_w_422;
+ u16 dst_h_420;
+};
+
+/* struct scaler_variant - SCALER variant information */
+struct scaler_variant {
+ struct scaler_frm_limit *pix_in;
+ struct scaler_frm_limit *pix_out;
+ struct scaler_pix_align *pix_align;
+ u16 scl_up_max;
+ u16 scl_down_max;
+ u16 in_buf_cnt;
+ u16 out_buf_cnt;
+};
+
+/**
+ * struct scaler_dev - abstraction for SCALER entity
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the SCALER platform device
+ * @variant: the IP variant information
+ * @id: SCALER device index (0..SCALER_MAX_DEVS)
+ * @clock: clocks required for SCALER operation
+ * @regs: the mapped hardware registers
+ * @irq_queue: interrupt handler waitqueue
+ * @m2m: memory-to-memory V4L2 device information
+ * @state: flags used to synchronize m2m and capture mode operation
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @vdev: video device for SCALER instance
+ */
+struct scaler_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct scaler_variant *variant;
+ struct clk *clock;
+ void __iomem *regs;
+ wait_queue_head_t irq_queue;
+ struct scaler_m2m_device m2m;
+ unsigned long state;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ enum scaler_csc_coeff coeff_type;
+};
+
+/**
+ * scaler_ctx - the device context data
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @scaler: image scaler properties
+ * @flags: additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @scaler_dev: the SCALER device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrl_handler: v4l2 controls handler
+ * @ctrls_scaler: SCALER control set
+ * @ctrls_rdy: true if the control handler is initialized
+ */
+struct scaler_ctx {
+ struct scaler_frame s_frame;
+ struct scaler_frame d_frame;
+ struct scaler_scaler scaler;
+ u32 flags;
+ u32 state;
+ int rotation;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ struct scaler_dev *scaler_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct scaler_ctrls ctrls_scaler;
+ bool ctrls_rdy;
+};
+
+static inline void scaler_ctx_state_lock_set(u32 state, struct scaler_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->scaler_dev->slock, flags);
+ ctx->state |= state;
+ spin_unlock_irqrestore(&ctx->scaler_dev->slock, flags);
+}
+
+static inline void scaler_ctx_state_lock_clear(u32 state,
+ struct scaler_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->scaler_dev->slock, flags);
+ ctx->state &= ~state;
+ spin_unlock_irqrestore(&ctx->scaler_dev->slock, flags);
+}
+
+static inline bool scaler_ctx_state_is_set(u32 mask, struct scaler_ctx *ctx)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&ctx->scaler_dev->slock, flags);
+ ret = (ctx->state & mask) == mask;
+ spin_unlock_irqrestore(&ctx->scaler_dev->slock, flags);
+ return ret;
+}
+
+void scaler_set_prefbuf(struct scaler_dev *scaler, struct scaler_frame *frm);
+int scaler_register_m2m_device(struct scaler_dev *scaler);
+void scaler_unregister_m2m_device(struct scaler_dev *scaler);
+void scaler_m2m_job_finish(struct scaler_ctx *ctx, int vb_state);
+u32 get_plane_size(struct scaler_frame *fr, unsigned int plane);
+const struct scaler_fmt *scaler_get_format(int index);
+const struct scaler_fmt *scaler_find_fmt(u32 *pixelformat, int index);
+struct scaler_frame *ctx_get_frame(struct scaler_ctx *ctx,
+ enum v4l2_buf_type type);
+int scaler_enum_fmt_mplane(struct v4l2_fmtdesc *f);
+int scaler_try_fmt_mplane(struct scaler_ctx *ctx, struct v4l2_format *f);
+void scaler_set_frame_size(struct scaler_frame *frame, int width, int height);
+int scaler_g_fmt_mplane(struct scaler_ctx *ctx, struct v4l2_format *f);
+void scaler_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
+int scaler_g_crop(struct scaler_ctx *ctx, struct v4l2_crop *cr);
+int scaler_try_crop(struct scaler_ctx *ctx, struct v4l2_crop *cr);
+int scaler_cal_prescaler_ratio(struct scaler_variant *var, u32 src, u32 dst,
+ u32 *ratio);
+void scaler_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
+void scaler_check_src_scale_info(struct scaler_variant *var,
+ struct scaler_frame *s_frame,
+ u32 *wratio, u32 tx, u32 ty, u32 *hratio);
+int scaler_check_scaler_ratio(struct scaler_variant *var, int sw, int sh,
+ int dw, int dh, int rot);
+int scaler_set_scaler_info(struct scaler_ctx *ctx);
+int scaler_ctrls_create(struct scaler_ctx *ctx);
+void scaler_ctrls_delete(struct scaler_ctx *ctx);
+int scaler_prepare_addr(struct scaler_ctx *ctx, struct vb2_buffer *vb,
+ struct scaler_frame *frame, struct scaler_addr *addr);
+
+#endif /* SCALER_CORE_H_ */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 3/4] [media] exynos-scaler: Add m2m functionality for the SCALER driver
2014-01-09 3:28 [PATCH v5 0/4] Exynos5 Series SCALER Driver Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver Shaik Ameer Basha
@ 2014-01-09 3:28 ` Shaik Ameer Basha
2014-01-09 8:31 ` Bartlomiej Zolnierkiewicz
2014-01-09 3:28 ` [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for " Shaik Ameer Basha
3 siblings, 1 reply; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-09 3:28 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the Makefile and memory to memory (m2m) interface
functionality for the SCALER driver.
[arun.kk at samsung.com: fix compilation issues]
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
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 +++++++++++++++++++++
4 files changed, 800 insertions(+)
create mode 100644 drivers/media/platform/exynos-scaler/Makefile
create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index b2a4403..aec5b80 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -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 OF && VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5
+ 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
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index e5269da..0f774ba 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -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/
diff --git a/drivers/media/platform/exynos-scaler/Makefile b/drivers/media/platform/exynos-scaler/Makefile
new file mode 100644
index 0000000..6c8a25b
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/Makefile
@@ -0,0 +1,3 @@
+exynos-scaler-objs := scaler.o scaler-m2m.o scaler-regs.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_SCALER) += exynos-scaler.o
diff --git a/drivers/media/platform/exynos-scaler/scaler-m2m.c b/drivers/media/platform/exynos-scaler/scaler-m2m.c
new file mode 100644
index 0000000..892b93b
--- /dev/null
+++ b/drivers/media/platform/exynos-scaler/scaler-m2m.c
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 2013 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);
+ }
+}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for SCALER driver
2014-01-09 3:28 [PATCH v5 0/4] Exynos5 Series SCALER Driver Shaik Ameer Basha
` (2 preceding siblings ...)
2014-01-09 3:28 ` [PATCH v5 3/4] [media] exynos-scaler: Add m2m " Shaik Ameer Basha
@ 2014-01-09 3:28 ` Shaik Ameer Basha
2014-01-09 9:20 ` Bartlomiej Zolnierkiewicz
2014-01-24 16:09 ` Tomasz Figa
3 siblings, 2 replies; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-09 3:28 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the DT binding documentation for the
Exynos5420/5410 based SCALER device driver.
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
.../devicetree/bindings/media/exynos5-scaler.txt | 22 ++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
diff --git a/Documentation/devicetree/bindings/media/exynos5-scaler.txt b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
new file mode 100644
index 0000000..9328e7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
@@ -0,0 +1,22 @@
+* 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 number
+- clocks: should contain the SCALER clock specifier, from the
+ common clock bindings
+- clock-names: should be "scaler"
+
+Example:
+ scaler_0: scaler at 12800000 {
+ compatible = "samsung,exynos5420-scaler";
+ reg = <0x12800000 0x1000>;
+ interrupts = <0 220 0>;
+ clocks = <&clock 381>;
+ clock-names = "scaler";
+ };
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 3/4] [media] exynos-scaler: Add m2m functionality for the SCALER driver
2014-01-09 3:28 ` [PATCH v5 3/4] [media] exynos-scaler: Add m2m " Shaik Ameer Basha
@ 2014-01-09 8:31 ` Bartlomiej Zolnierkiewicz
0 siblings, 0 replies; 11+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2014-01-09 8:31 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Thursday, January 09, 2014 08:58:13 AM Shaik Ameer Basha wrote:
> This patch adds the Makefile and memory to memory (m2m) interface
> functionality for the SCALER driver.
>
> [arun.kk at samsung.com: fix compilation issues]
>
> Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
> Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> 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 +++++++++++++++++++++
It would be cleaner to add Kconfig + Makefiles in the same patch
that adds core functionality (patch #2) and then switch the order of
patch #2 and patch #3.
> 4 files changed, 800 insertions(+)
> create mode 100644 drivers/media/platform/exynos-scaler/Makefile
> create mode 100644 drivers/media/platform/exynos-scaler/scaler-m2m.c
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index b2a4403..aec5b80 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -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 OF && VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5
Please check for EXYNOS5410 and EXYNOS5420 explicitly instead
of checking just for ARCH_EXYNOS5.
Also this config option doesn't need to depend on OF since
the whole EXYNOS support is OF only now.
> + 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
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver
2014-01-09 3:28 ` [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver Shaik Ameer Basha
@ 2014-01-09 8:35 ` Bartlomiej Zolnierkiewicz
0 siblings, 0 replies; 11+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2014-01-09 8:35 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Thursday, January 09, 2014 08:58:12 AM Shaik Ameer Basha wrote:
> This patch adds the core functionality for the SCALER driver.
>
> Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> drivers/media/platform/exynos-scaler/scaler.c | 1231 +++++++++++++++++++++++++
> drivers/media/platform/exynos-scaler/scaler.h | 376 ++++++++
> 2 files changed, 1607 insertions(+)
> create mode 100644 drivers/media/platform/exynos-scaler/scaler.c
> create mode 100644 drivers/media/platform/exynos-scaler/scaler.h
[...]
> +static int scaler_probe(struct platform_device *pdev)
> +{
> + struct scaler_dev *scaler;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + if (!dev->of_node)
> + return -ENODEV;
> +
> + scaler = devm_kzalloc(dev, sizeof(*scaler), GFP_KERNEL);
> + if (!scaler)
> + return -ENOMEM;
> +
> + scaler->pdev = pdev;
> + scaler->variant = scaler_get_variant_data(pdev);
> +
> + init_waitqueue_head(&scaler->irq_queue);
> + spin_lock_init(&scaler->slock);
> + mutex_init(&scaler->lock);
> + scaler->clock = ERR_PTR(-EINVAL);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + scaler->regs = devm_request_and_ioremap(dev, res);
> + if (!scaler->regs)
> + return -ENODEV;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!res) {
> + dev_err(dev, "failed to get IRQ resource\n");
> + return -ENXIO;
> + }
> +
> + ret = scaler_clk_get(scaler);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_request_irq(dev, res->start, scaler_irq_handler,
> + 0, pdev->name, scaler);
> + if (ret < 0) {
> + dev_err(dev, "failed to install irq (%d)\n", ret);
> + goto err_clk;
> + }
> +
> + platform_set_drvdata(pdev, scaler);
> + pm_runtime_enable(dev);
> +
> + /* Initialize the continious memory allocator */
> + scaler->alloc_ctx = vb2_dma_contig_init_ctx(dev);
> + if (IS_ERR(scaler->alloc_ctx)) {
> + ret = PTR_ERR(scaler->alloc_ctx);
> + goto err_clk;
> + }
> +
> + ret = v4l2_device_register(dev, &scaler->v4l2_dev);
> + if (ret < 0)
> + goto err_clk;
> +
> + ret = scaler_register_m2m_device(scaler);
> + if (ret < 0)
> + goto err_v4l2;
> +
> + dev_info(dev, "registered successfully\n");
> + return 0;
> +
> +err_v4l2:
> + v4l2_device_unregister(&scaler->v4l2_dev);
> +err_clk:
> + scaler_clk_put(scaler);
vb2_dma_contig_cleanup_ctx() and pm_runtime_disable() calls on
failure are missing
> + return ret;
> +}
> +
> +static int scaler_remove(struct platform_device *pdev)
> +{
> + struct scaler_dev *scaler = platform_get_drvdata(pdev);
> +
> + scaler_unregister_m2m_device(scaler);
> + v4l2_device_unregister(&scaler->v4l2_dev);
> +
> + vb2_dma_contig_cleanup_ctx(scaler->alloc_ctx);
> + pm_runtime_disable(&pdev->dev);
> + scaler_clk_put(scaler);
> +
> + scaler_dbg(scaler, "%s driver unloaded\n", pdev->name);
> + return 0;
> +}
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for SCALER driver
2014-01-09 3:28 ` [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for " Shaik Ameer Basha
@ 2014-01-09 9:20 ` Bartlomiej Zolnierkiewicz
2014-01-20 14:34 ` Shaik Ameer Basha
2014-01-24 16:09 ` Tomasz Figa
1 sibling, 1 reply; 11+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2014-01-09 9:20 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Thursday, January 09, 2014 08:58:14 AM 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 <shaik.ameer@samsung.com>
> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> .../devicetree/bindings/media/exynos5-scaler.txt | 22 ++++++++++++++++++++
> 1 file changed, 22 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
>
> diff --git a/Documentation/devicetree/bindings/media/exynos5-scaler.txt b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
> new file mode 100644
> index 0000000..9328e7d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
> @@ -0,0 +1,22 @@
> +* 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 number
> +- clocks: should contain the SCALER clock specifier, from the
> + common clock bindings
> +- clock-names: should be "scaler"
> +
> +Example:
> + scaler_0: scaler at 12800000 {
> + compatible = "samsung,exynos5420-scaler";
> + reg = <0x12800000 0x1000>;
> + interrupts = <0 220 0>;
> + clocks = <&clock 381>;
> + clock-names = "scaler";
> + };
Your patchset adds support for EXYNOS5 SCALER but doesn't add any real
users of it yet. Could you please explain why?
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for SCALER driver
2014-01-09 9:20 ` Bartlomiej Zolnierkiewicz
@ 2014-01-20 14:34 ` Shaik Ameer Basha
0 siblings, 0 replies; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-20 14:34 UTC (permalink / raw)
To: linux-arm-kernel
Hi Bartlomiej,
Thanks for the review.
Yes you are right. I didn't add the users for this driver.
Once the driver gets merged, I will send more patches with the users.
Already this driver merge is pending on DT maintainers ack and I
don't want to complex it more by adding DT patches :)
Definitely, I will send the users patches once the driver gets merged.
And I will address all your comments in next version of patch series.
Regards,
Shaik Ameer Basha
On Thu, Jan 9, 2014 at 6:20 PM, Bartlomiej Zolnierkiewicz
<b.zolnierkie@samsung.com> wrote:
>
> Hi,
>
> On Thursday, January 09, 2014 08:58:14 AM 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 <shaik.ameer@samsung.com>
>> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> ---
>> .../devicetree/bindings/media/exynos5-scaler.txt | 22 ++++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/exynos5-scaler.txt b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
>> new file mode 100644
>> index 0000000..9328e7d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
>> @@ -0,0 +1,22 @@
>> +* 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 number
>> +- clocks: should contain the SCALER clock specifier, from the
>> + common clock bindings
>> +- clock-names: should be "scaler"
>> +
>> +Example:
>> + scaler_0: scaler at 12800000 {
>> + compatible = "samsung,exynos5420-scaler";
>> + reg = <0x12800000 0x1000>;
>> + interrupts = <0 220 0>;
>> + clocks = <&clock 381>;
>> + clock-names = "scaler";
>> + };
>
> Your patchset adds support for EXYNOS5 SCALER but doesn't add any real
> users of it yet. Could you please explain why?
>
> Best regards,
> --
> Bartlomiej Zolnierkiewicz
> Samsung R&D Institute Poland
> Samsung Electronics
>
> --
> 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
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for SCALER driver
2014-01-09 3:28 ` [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for " Shaik Ameer Basha
2014-01-09 9:20 ` Bartlomiej Zolnierkiewicz
@ 2014-01-24 16:09 ` Tomasz Figa
2014-01-31 6:47 ` Shaik Ameer Basha
1 sibling, 1 reply; 11+ messages in thread
From: Tomasz Figa @ 2014-01-24 16:09 UTC (permalink / raw)
To: linux-arm-kernel
Hi Shaik,
On 09.01.2014 04:28, 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 <shaik.ameer@samsung.com>
> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> .../devicetree/bindings/media/exynos5-scaler.txt | 22 ++++++++++++++++++++
> 1 file changed, 22 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
>
> diff --git a/Documentation/devicetree/bindings/media/exynos5-scaler.txt b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
> new file mode 100644
> index 0000000..9328e7d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
> @@ -0,0 +1,22 @@
> +* 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 number
s/number/specifier/
> +- clocks: should contain the SCALER clock specifier, from the
> + common clock bindings
s/specifier/phandle and specifier pair for each clock listed in
clock-names property/
s/from/according to/
> +- clock-names: should be "scaler"
should contain exactly one entry:
- "scaler" - IP bus clock.
Also this patch should be first in the series to let the driver added in
further patches use already present bindings.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for SCALER driver
2014-01-24 16:09 ` Tomasz Figa
@ 2014-01-31 6:47 ` Shaik Ameer Basha
0 siblings, 0 replies; 11+ messages in thread
From: Shaik Ameer Basha @ 2014-01-31 6:47 UTC (permalink / raw)
To: linux-arm-kernel
Hi Tomasz,
Thanks for the review.
Will consider all your comments in the next version of patch series.
Regards,
Shaik Ameer Basha
On Sat, Jan 25, 2014 at 1:09 AM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Shaik,
>
>
> On 09.01.2014 04:28, 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 <shaik.ameer@samsung.com>
>> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> ---
>> .../devicetree/bindings/media/exynos5-scaler.txt | 22
>> ++++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>> create mode 100644
>> Documentation/devicetree/bindings/media/exynos5-scaler.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/exynos5-scaler.txt
>> b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
>> new file mode 100644
>> index 0000000..9328e7d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/exynos5-scaler.txt
>> @@ -0,0 +1,22 @@
>> +* 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 number
>
>
> s/number/specifier/
>
>
>> +- clocks: should contain the SCALER clock specifier, from the
>> + common clock bindings
>
>
> s/specifier/phandle and specifier pair for each clock listed in clock-names
> property/
>
> s/from/according to/
>
>
>> +- clock-names: should be "scaler"
>
>
> should contain exactly one entry:
> - "scaler" - IP bus clock.
>
> Also this patch should be first in the series to let the driver added in
> further patches use already present bindings.
>
> Best regards,
> Tomasz
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> in
>
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2014-01-31 6:47 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-09 3:28 [PATCH v5 0/4] Exynos5 Series SCALER Driver Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER Shaik Ameer Basha
2014-01-09 3:28 ` [PATCH v5 2/4] [media] exynos-scaler: Add core functionality for the SCALER driver Shaik Ameer Basha
2014-01-09 8:35 ` Bartlomiej Zolnierkiewicz
2014-01-09 3:28 ` [PATCH v5 3/4] [media] exynos-scaler: Add m2m " Shaik Ameer Basha
2014-01-09 8:31 ` Bartlomiej Zolnierkiewicz
2014-01-09 3:28 ` [PATCH v5 4/4] [media] exynos-scaler: Add DT bindings for " Shaik Ameer Basha
2014-01-09 9:20 ` Bartlomiej Zolnierkiewicz
2014-01-20 14:34 ` Shaik Ameer Basha
2014-01-24 16:09 ` Tomasz Figa
2014-01-31 6:47 ` Shaik Ameer Basha
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).