Message ID | 1380889594-10448-3-git-send-email-shaik.ameer@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 04/10/13 14:26, 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> Just couple minor comment below... > --- > drivers/media/platform/exynos-scaler/scaler.c | 1238 +++++++++++++++++++++++++ > drivers/media/platform/exynos-scaler/scaler.h | 375 ++++++++ > 2 files changed, 1613 insertions(+) > create mode 100644 drivers/media/platform/exynos-scaler/scaler.c > create mode 100644 drivers/media/platform/exynos-scaler/scaler.h [...] > +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) > +{ > + struct scaler_dev *scaler = ctx->scaler_dev; > + struct scaler_variant *variant = scaler->variant; > + int ret = 0; > + > + 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: > + ret = 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); I think this check is not needed, since it is already done in __scaler_try_ctrl() ? > + if (ret < 0) > + return -EINVAL; > + > + 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); > + scaler_dbg(ctx->scaler_dev, This should probably be dev_err(). > + "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); Alignment seems wrong here. > + 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); Perhaps remove "~~~" ? > + 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; > +} Thanks, Sylwester -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Em Fri, 4 Oct 2013 17:56:32 +0530 Shaik Ameer Basha <shaik.ameer@samsung.com> escreveu: > This patch adds the core functionality for the SCALER driver. > > Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com> > --- > drivers/media/platform/exynos-scaler/scaler.c | 1238 +++++++++++++++++++++++++ > drivers/media/platform/exynos-scaler/scaler.h | 375 ++++++++ > 2 files changed, 1613 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..57635f2 > --- /dev/null > +++ b/drivers/media/platform/exynos-scaler/scaler.c > @@ -0,0 +1,1238 @@ > +/* > + * 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), Not a big deal, but you don't need parenthesis for any of those .flags initialization. > + > + }, { > + .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) > +{ > + struct scaler_dev *scaler = ctx->scaler_dev; > + struct scaler_variant *variant = scaler->variant; > + int ret = 0; > + > + 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: > + ret = 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); > + > + if (ret < 0) > + return -EINVAL; > + > + 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); > + scaler_dbg(ctx->scaler_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 = scaler_register_m2m_device(scaler); > + if (ret < 0) > + goto err_clk; > + > + dev_info(dev, "registered successfully\n"); > + return 0; > + > +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); > + > + 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..47f6ee0 > --- /dev/null > +++ b/drivers/media/platform/exynos-scaler/scaler.h > @@ -0,0 +1,375 @@ > +/* > + * 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; > + 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_ */
Hi Mauro, Thanks for the reveiw. On Fri, Jan 3, 2014 at 1:55 AM, Mauro Carvalho Chehab <m.chehab@samsung.com> wrote: > Em Fri, 4 Oct 2013 17:56:32 +0530 > Shaik Ameer Basha <shaik.ameer@samsung.com> escreveu: > >> This patch adds the core functionality for the SCALER driver. >> >> Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com> >> --- >> drivers/media/platform/exynos-scaler/scaler.c | 1238 +++++++++++++++++++++++++ >> drivers/media/platform/exynos-scaler/scaler.h | 375 ++++++++ >> 2 files changed, 1613 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..57635f2 >> --- /dev/null >> +++ b/drivers/media/platform/exynos-scaler/scaler.c >> @@ -0,0 +1,1238 @@ >> +/* >> + * 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), > > Not a big deal, but you don't need parenthesis for any of those .flags > initialization. > Ok. I will fix this. and all other comments given by you in this patch series. Regards, Shaik Ameer Basha [...] Snip > > -- > > Cheers, > Mauro > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/media/platform/exynos-scaler/scaler.c b/drivers/media/platform/exynos-scaler/scaler.c new file mode 100644 index 0000000..57635f2 --- /dev/null +++ b/drivers/media/platform/exynos-scaler/scaler.c @@ -0,0 +1,1238 @@ +/* + * 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) +{ + struct scaler_dev *scaler = ctx->scaler_dev; + struct scaler_variant *variant = scaler->variant; + int ret = 0; + + 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: + ret = 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); + + if (ret < 0) + return -EINVAL; + + 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); + scaler_dbg(ctx->scaler_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 = scaler_register_m2m_device(scaler); + if (ret < 0) + goto err_clk; + + dev_info(dev, "registered successfully\n"); + return 0; + +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); + + 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..47f6ee0 --- /dev/null +++ b/drivers/media/platform/exynos-scaler/scaler.h @@ -0,0 +1,375 @@ +/* + * 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; + 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_ */
This patch adds the core functionality for the SCALER driver. Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com> --- drivers/media/platform/exynos-scaler/scaler.c | 1238 +++++++++++++++++++++++++ drivers/media/platform/exynos-scaler/scaler.h | 375 ++++++++ 2 files changed, 1613 insertions(+) create mode 100644 drivers/media/platform/exynos-scaler/scaler.c create mode 100644 drivers/media/platform/exynos-scaler/scaler.h