Skip to contentSkip to conversation

Exynos5 Series SCALER Driver

PATCH Superseded Shaik Ameer Basha 4 participants 11 messages Jan 9, 2014 – Jan 31, 2014

Subsystems (3)

v5 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v5 cover Exynos5 Series SCALER Driver

This patch adds support for SCALER device which is a new device for scaling, blending, color fill and color space conversion on EXYNOS5410/5420 SoCs.

This device supports the following as key features.
input image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888, L8A8 and L8
output image format
- YCbCr420 2P(UV/VU), 3P
- YCbCr422 1P(YUYV/UYVY/YVYU), 2P(UV,VU), 3P
- YCbCr444 2P(UV,VU), 3P
- RGB565, ARGB1555, ARGB4444, ARGB8888, RGBA8888
- Pre-multiplexed ARGB8888
input rotation
- 0/90/180/270 degree, X/Y/XY Flip
scale ratio
- 1/4 scale down to 16 scale up
color space conversion
- RGB to YUV / YUV to RGB
Size - Exynos5420
- Input : 16x16 to 8192x8192
- Output: 4x4 to 8192x8192
Size - Exynos5410
- Input/Output: 4x4 to 4096x4096
alpha blending, color fill

Rebased on:
-----------
git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git:master

Changes from 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
Signature · 1 line
1.7.9.5
#1 lore raw

PATCH v5 1/4 exynos-scaler: Add new driver for Exynos5 SCALER

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

---
 drivers/media/platform/exynos-scaler/scaler-regs.c |  337 ++++++++++++++++++++
 drivers/media/platform/exynos-scaler/scaler-regs.h |  331 +++++++++++++++++++
 2 files changed, 668 insertions(+)
 create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.c
 create mode 100644 drivers/media/platform/exynos-scaler/scaler-regs.h
drivers/media/platform/exynos-scaler/scaler-regs.c new file +337 -0
new file mode 100644
index 0000000..8f5076a
@@ -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);
+}
drivers/media/platform/exynos-scaler/scaler-regs.h new file +331 -0

331 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#2 lore raw

PATCH v5 2/4 exynos-scaler: Add core functionality for the SCALER driver

In reply to Shaik Ameer Basha

This patch adds the core functionality for the SCALER driver.

---
 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
drivers/media/platform/exynos-scaler/scaler.c new file +1231 -0
new file mode 100644
index 0000000..db2cad1
@@ -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 <[email protected]>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series SCALER driver");
+MODULE_LICENSE("GPL v2");
drivers/media/platform/exynos-scaler/scaler.h new file +376 -0

376 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#3 lore raw

PATCH v5 2/4 exynos-scaler: Add core functionality for the SCALER driver

Hi,

Quoted 10 lines · Shaik Ameer Basha

This patch adds the core functionality for the SCALER driver.

---
 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

[...]

Quoted 59 lines
drivers/media/platform/exynos-scaler/scaler.c new file +59 -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);

drivers/media/platform/exynos-scaler/scaler.c line 1128

Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler.c new file +10 -0
+	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

Quoted 7 lines
drivers/media/platform/exynos-scaler/scaler.c new file +7 -0
+	return ret;
+}
+
+static int scaler_remove(struct platform_device *pdev)
+{
+	struct scaler_dev *scaler = platform_get_drvdata(pdev);
+

drivers/media/platform/exynos-scaler/scaler.c line 1145

Quoted 10 lines
drivers/media/platform/exynos-scaler/scaler.c new file +10 -0
+	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

#4 lore raw

PATCH v5 3/4 exynos-scaler: Add m2m functionality for the SCALER driver

In reply to Shaik Ameer Basha

This patch adds the Makefile and memory to memory (m2m) interface functionality for the SCALER driver.

[arun.kk at samsung.com: fix compilation issues]

---
 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
drivers/media/platform/Kconfig +8 -0
index b2a4403..aec5b80 100644
@@ -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
drivers/media/platform/Makefile +1 -0
index e5269da..0f774ba 100644
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
 
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)	+= s5p-g2d/
 obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC)	+= exynos-gsc/
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_SCALER)	+= exynos-scaler/
 
 obj-$(CONFIG_BLACKFIN)                  += blackfin/
 
drivers/media/platform/exynos-scaler/Makefile new file +3 -0
new file mode 100644
index 0000000..6c8a25b
@@ -0,0 +1,3 @@
+exynos-scaler-objs := scaler.o scaler-m2m.o scaler-regs.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_SCALER)	+= exynos-scaler.o
drivers/media/platform/exynos-scaler/scaler-m2m.c new file +788 -0

788 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#5 lore raw

PATCH v5 3/4 exynos-scaler: Add m2m functionality for the SCALER driver

Hi,

Quoted 5 lines · Shaik Ameer Basha

This patch adds the Makefile and memory to memory (m2m) interface functionality for the SCALER driver.

[arun.kk at samsung.com: fix compilation issues]

Quoted 8 lines
---
 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.

Quoted 9 lines
 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
drivers/media/platform/Kconfig +0 -0
index b2a4403..aec5b80 100644

drivers/media/platform/Kconfig config VIDEO_SAMSUNG_EXYNOS_GSC · line 201

Quoted 6 lines
drivers/media/platform/Kconfig +3 -0
@@ -196,6 +196,14 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
 	help
 	  This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
 
+config VIDEO_SAMSUNG_EXYNOS_SCALER
+	tristate "Samsung Exynos SCALER driver"
+	depends on 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.

drivers/media/platform/Kconfig config VIDEO_SAMSUNG_EXYNOS_GSC · line 209

Quoted 8 lines
drivers/media/platform/Kconfig +5 -0
+	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

#6 lore raw

PATCH v5 4/4 exynos-scaler: Add DT bindings for SCALER driver

In reply to Shaik Ameer Basha

This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.

---
 .../devicetree/bindings/media/exynos5-scaler.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +22 -0
new file mode 100644
index 0000000..9328e7d
@@ -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";
+	};
Signature · 1 line
1.7.9.5
#7 lore raw

PATCH v5 4/4 exynos-scaler: Add DT bindings for SCALER driver

Hi,

Quoted 28 lines · Shaik Ameer Basha

This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.

---
 .../devicetree/bindings/media/exynos5-scaler.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +12 -0
new file mode 100644
index 0000000..9328e7d
@@ -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

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 22

Quoted 10 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10 -0
+- 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

#8 lore raw

PATCH v5 4/4 exynos-scaler: Add DT bindings for SCALER driver

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

Quoted 56 lines · Bartlomiej Zolnierkiewicz

Hi,

Quoted 28 lines · Shaik Ameer Basha

This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.

---
 .../devicetree/bindings/media/exynos5-scaler.txt   |   22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +12 -0
new file mode 100644
index 0000000..9328e7d
@@ -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

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 22

Quoted 10 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10 -0
+- 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

#9 lore raw

PATCH v5 4/4 exynos-scaler: Add DT bindings for SCALER driver

In reply to Shaik Ameer Basha

Hi Shaik,

Quoted 16 lines · Shaik Ameer Basha

This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.

---
  .../devicetree/bindings/media/exynos5-scaler.txt   |   22 ++++++++++++++++++++
  1 file changed, 22 insertions(+)

create mode 100644 Documentation/devicetree/bindings/media/exynos5-scaler.txt

Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +0 -0
new file mode 100644
index 0000000..9328e7d

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 10

Quoted 10 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10 -0
@@ -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/

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 12

Quoted 2 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +2 -0
+- 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/

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 13

Quoted 1 line
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +1 -0
+- 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

#10 lore raw

PATCH v5 4/4 exynos-scaler: Add DT bindings for SCALER driver

Hi Tomasz,

Thanks for the review.
Will consider all your comments in the next version of patch series.

Regards,
Shaik Ameer Basha

Quoted 66 lines · Tomasz Figa

Hi Shaik,

Quoted 20 lines · Shaik Ameer Basha

This patch adds the DT binding documentation for the Exynos5420/5410 based SCALER device driver.

---
  .../devicetree/bindings/media/exynos5-scaler.txt   |   22
++++++++++++++++++++
  1 file changed, 22 insertions(+)

create mode 100644
Documentation/devicetree/bindings/media/exynos5-scaler.txt

Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +0 -0
new file mode 100644
index 0000000..9328e7d

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 10

Quoted 10 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +10 -0
@@ -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/

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 12

Quoted 2 lines
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +2 -0
+- 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/

Documentation/devicetree/bindings/media/exynos5-scaler.txt line 13

Quoted 1 line
Documentation/devicetree/bindings/media/exynos5-scaler.txt new file +1 -0
+- 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