From patchwork Fri Oct 4 12:26:31 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shaik Ameer Basha X-Patchwork-Id: 2988411 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C1BE2BFF0B for ; Fri, 4 Oct 2013 12:23:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D2F1C201BD for ; Fri, 4 Oct 2013 12:23:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D76D620304 for ; Fri, 4 Oct 2013 12:23:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754337Ab3JDMXj (ORCPT ); Fri, 4 Oct 2013 08:23:39 -0400 Received: from mail-pd0-f173.google.com ([209.85.192.173]:43771 "EHLO mail-pd0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754309Ab3JDMXg (ORCPT ); Fri, 4 Oct 2013 08:23:36 -0400 Received: by mail-pd0-f173.google.com with SMTP id p10so3928064pdj.18 for ; Fri, 04 Oct 2013 05:23:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=Fh/UOQvRWjgm/reK9qWm8TJUzuFyUiMGw15e4kfPHhw=; b=n9011iQc6MkVLnyLsDfYWgvRvVhL/sumajTOzfq0Mr6ybONrFgkKOrqfbmNXfXbcLn SGqA/Ph/cTV9sTBoK5mCbO15RONSX5dXuoxTsBzOhfxOKd34EPPHBtTg/yvnomf33F6b dNkAN06tddL/VUAhUR0GklX0mfLkhrEka5Jmw7J5Wc8pPlsmtl9sUJsSgA4uFFV1Kyn3 iXBgQFuXi8Cr22FGdP5ZnQjXalqIUkotxW++xZlKlR6UGlMDcV/Vts1bX2jW2BVtpYci JeM+Ljkm9b5YLSOsfoGIeLH40lss4gVIrQFYmdEpSqJqG+Lqvw2MyxWMKOLs+zuHtcfJ KwqA== X-Received: by 10.68.6.198 with SMTP id d6mr14176830pba.73.1380889415844; Fri, 04 Oct 2013 05:23:35 -0700 (PDT) Received: from localhost.localdomain ([115.113.119.130]) by mx.google.com with ESMTPSA id qp10sm17771412pab.13.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 04 Oct 2013 05:23:35 -0700 (PDT) From: Shaik Ameer Basha To: linux-media@vger.kernel.org, linux-samsung-soc@vger.kernel.org Cc: s.nawrocki@samsung.com, posciak@google.com, inki.dae@samsung.com, hverkuil@xs4all.nl, shaik.ameer@samsung.com Subject: [PATCH v4 1/4] [media] exynos-scaler: Add new driver for Exynos5 SCALER Date: Fri, 4 Oct 2013 17:56:31 +0530 Message-Id: <1380889594-10448-2-git-send-email-shaik.ameer@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1380889594-10448-1-git-send-email-shaik.ameer@samsung.com> References: <1380889594-10448-1-git-send-email-shaik.ameer@samsung.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Reviewed-by: Sylwester Nawrocki --- drivers/media/platform/exynos-scaler/scaler-regs.c | 336 ++++++++++++++++++++ drivers/media/platform/exynos-scaler/scaler-regs.h | 331 +++++++++++++++++++ 2 files changed, 667 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..ae4a548 --- /dev/null +++ b/drivers/media/platform/exynos-scaler/scaler-regs.c @@ -0,0 +1,336 @@ +/* + * 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 +#include + +#include "scaler-regs.h" + +/* Scaler reset timeout in milliseconds */ +#define SCALER_RESET_TIMEOUT 50 + +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_before(jiffies, end)) { + cfg = scaler_read(dev, SCALER_CFG); + if (!(cfg & SCALER_CFG_SOFT_RESET)) { + reset_done = 1; + break; + } + usleep_range(10, 20); + } + + /* + * Write any value to read/write register and read it back. + * If the written and read value matches, then the reset process is + * succeeded. + */ + while (reset_done) { + + /* + * TODO: need to define number of tries before returning + * -EBUSY to the caller + */ + + 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; + } + + 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, cropX: %d, cropY: %d\n", + frame->selection.left, frame->selection.top, + frame->f_width, frame->f_width, 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, crop X: %d, crop Y: %d\n", + frame->selection.left, frame->selection.top, + frame->f_width, frame->f_width, 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..2170df5 --- /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 at 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, "Illegal Blend setting"}, + {SCALER_INT_ILLEGAL_RATIO, "Illegal Scale ratio setting"}, + {SCALER_INT_ILLEGAL_DST_HEIGHT, "Illegal Dst Height"}, + {SCALER_INT_ILLEGAL_DST_WIDTH, "Illegal Dst Width"}, + {SCALER_INT_ILLEGAL_DST_V_POS, "Illegal Dst V-Pos"}, + {SCALER_INT_ILLEGAL_DST_H_POS, "Illegal Dst H-Pos"}, + {SCALER_INT_ILLEGAL_DST_C_SPAN, "Illegal Dst C-Span"}, + {SCALER_INT_ILLEGAL_DST_Y_SPAN, "Illegal Dst Y-span"}, + {SCALER_INT_ILLEGAL_DST_CR_BASE, "Illegal Dst Cr-base"}, + {SCALER_INT_ILLEGAL_DST_CB_BASE, "Illegal Dst Cb-base"}, + {SCALER_INT_ILLEGAL_DST_Y_BASE, "Illegal Dst Y-base"}, + {SCALER_INT_ILLEGAL_DST_COLOR, "Illegal Dst Color"}, + {SCALER_INT_ILLEGAL_SRC_HEIGHT, "Illegal Src Height"}, + {SCALER_INT_ILLEGAL_SRC_WIDTH, "Illegal Src Width"}, + {SCALER_INT_ILLEGAL_SRC_CV_POS, "Illegal Src Chroma V-pos"}, + {SCALER_INT_ILLEGAL_SRC_CH_POS, "Illegal Src Chroma H-pos"}, + {SCALER_INT_ILLEGAL_SRC_YV_POS, "Illegal Src Luma V-pos"}, + {SCALER_INT_ILLEGAL_SRC_YH_POS, "Illegal Src Luma H-pos"}, + {SCALER_INT_ILLEGAL_SRC_C_SPAN, "Illegal Src C-span"}, + {SCALER_INT_ILLEGAL_SRC_Y_SPAN, "Illegal Src Y-span"}, + {SCALER_INT_ILLEGAL_SRC_CR_BASE, "Illegal Src Cr-base"}, + {SCALER_INT_ILLEGAL_SRC_CB_BASE, "Illegal Src Cb-base"}, + {SCALER_INT_ILLEGAL_SRC_Y_BASE, "Illegal Src Y-base"}, + {SCALER_INT_ILLEGAL_SRC_COLOR, "Illegal 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_ */