linux/drivers/media/platform/sti/bdisp/bdisp-hw.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) STMicroelectronics SA 2014
   3 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
   4 * License terms:  GNU General Public License (GPL), version 2
   5 */
   6
   7#include <linux/delay.h>
   8
   9#include "bdisp.h"
  10#include "bdisp-filter.h"
  11#include "bdisp-reg.h"
  12
  13/* Max width of the source frame in a single node */
  14#define MAX_SRC_WIDTH           2048
  15
  16/* Reset & boot poll config */
  17#define POLL_RST_MAX            50
  18#define POLL_RST_DELAY_MS       20
  19
  20enum bdisp_target_plan {
  21        BDISP_RGB,
  22        BDISP_Y,
  23        BDISP_CBCR
  24};
  25
  26struct bdisp_op_cfg {
  27        bool cconv;          /* RGB - YUV conversion */
  28        bool hflip;          /* Horizontal flip */
  29        bool vflip;          /* Vertical flip */
  30        bool wide;           /* Wide (>MAX_SRC_WIDTH) */
  31        bool scale;          /* Scale */
  32        u16  h_inc;          /* Horizontal increment in 6.10 format */
  33        u16  v_inc;          /* Vertical increment in 6.10 format */
  34        bool src_interlaced; /* is the src an interlaced buffer */
  35        u8   src_nbp;        /* nb of planes of the src */
  36        bool src_yuv;        /* is the src a YUV color format */
  37        bool src_420;        /* is the src 4:2:0 chroma subsampled */
  38        u8   dst_nbp;        /* nb of planes of the dst */
  39        bool dst_yuv;        /* is the dst a YUV color format */
  40        bool dst_420;        /* is the dst 4:2:0 chroma subsampled */
  41};
  42
  43struct bdisp_filter_addr {
  44        u16 min;             /* Filter min scale factor (6.10 fixed point) */
  45        u16 max;             /* Filter max scale factor (6.10 fixed point) */
  46        void *virt;          /* Virtual address for filter table */
  47        dma_addr_t paddr;    /* Physical address for filter table */
  48};
  49
  50static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER];
  51static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER];
  52
  53/**
  54 * bdisp_hw_reset
  55 * @bdisp:      bdisp entity
  56 *
  57 * Resets HW
  58 *
  59 * RETURNS:
  60 * 0 on success.
  61 */
  62int bdisp_hw_reset(struct bdisp_dev *bdisp)
  63{
  64        unsigned int i;
  65
  66        dev_dbg(bdisp->dev, "%s\n", __func__);
  67
  68        /* Mask Interrupt */
  69        writel(0, bdisp->regs + BLT_ITM0);
  70
  71        /* Reset */
  72        writel(readl(bdisp->regs + BLT_CTL) | BLT_CTL_RESET,
  73               bdisp->regs + BLT_CTL);
  74        writel(0, bdisp->regs + BLT_CTL);
  75
  76        /* Wait for reset done */
  77        for (i = 0; i < POLL_RST_MAX; i++) {
  78                if (readl(bdisp->regs + BLT_STA1) & BLT_STA1_IDLE)
  79                        break;
  80                msleep(POLL_RST_DELAY_MS);
  81        }
  82        if (i == POLL_RST_MAX)
  83                dev_err(bdisp->dev, "Reset timeout\n");
  84
  85        return (i == POLL_RST_MAX) ? -EAGAIN : 0;
  86}
  87
  88/**
  89 * bdisp_hw_get_and_clear_irq
  90 * @bdisp:      bdisp entity
  91 *
  92 * Read then reset interrupt status
  93 *
  94 * RETURNS:
  95 * 0 if expected interrupt was raised.
  96 */
  97int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp)
  98{
  99        u32 its;
 100
 101        its = readl(bdisp->regs + BLT_ITS);
 102
 103        /* Check for the only expected IT: LastNode of AQ1 */
 104        if (!(its & BLT_ITS_AQ1_LNA)) {
 105                dev_dbg(bdisp->dev, "Unexpected IT status: 0x%08X\n", its);
 106                writel(its, bdisp->regs + BLT_ITS);
 107                return -1;
 108        }
 109
 110        /* Clear and mask */
 111        writel(its, bdisp->regs + BLT_ITS);
 112        writel(0, bdisp->regs + BLT_ITM0);
 113
 114        return 0;
 115}
 116
 117/**
 118 * bdisp_hw_free_nodes
 119 * @ctx:        bdisp context
 120 *
 121 * Free node memory
 122 *
 123 * RETURNS:
 124 * None
 125 */
 126void bdisp_hw_free_nodes(struct bdisp_ctx *ctx)
 127{
 128        if (ctx && ctx->node[0]) {
 129                DEFINE_DMA_ATTRS(attrs);
 130
 131                dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 132                dma_free_attrs(ctx->bdisp_dev->dev,
 133                               sizeof(struct bdisp_node) * MAX_NB_NODE,
 134                               ctx->node[0], ctx->node_paddr[0], &attrs);
 135        }
 136}
 137
 138/**
 139 * bdisp_hw_alloc_nodes
 140 * @ctx:        bdisp context
 141 *
 142 * Allocate dma memory for nodes
 143 *
 144 * RETURNS:
 145 * 0 on success
 146 */
 147int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx)
 148{
 149        struct device *dev = ctx->bdisp_dev->dev;
 150        unsigned int i, node_size = sizeof(struct bdisp_node);
 151        void *base;
 152        dma_addr_t paddr;
 153        DEFINE_DMA_ATTRS(attrs);
 154
 155        /* Allocate all the nodes within a single memory page */
 156        dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 157        base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr,
 158                               GFP_KERNEL | GFP_DMA, &attrs);
 159        if (!base) {
 160                dev_err(dev, "%s no mem\n", __func__);
 161                return -ENOMEM;
 162        }
 163
 164        memset(base, 0, node_size * MAX_NB_NODE);
 165
 166        for (i = 0; i < MAX_NB_NODE; i++) {
 167                ctx->node[i] = base;
 168                ctx->node_paddr[i] = paddr;
 169                dev_dbg(dev, "node[%d]=0x%p (paddr=%pad)\n", i, ctx->node[i],
 170                        &paddr);
 171                base += node_size;
 172                paddr += node_size;
 173        }
 174
 175        return 0;
 176}
 177
 178/**
 179 * bdisp_hw_free_filters
 180 * @dev:        device
 181 *
 182 * Free filters memory
 183 *
 184 * RETURNS:
 185 * None
 186 */
 187void bdisp_hw_free_filters(struct device *dev)
 188{
 189        int size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
 190
 191        if (bdisp_h_filter[0].virt) {
 192                DEFINE_DMA_ATTRS(attrs);
 193
 194                dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 195                dma_free_attrs(dev, size, bdisp_h_filter[0].virt,
 196                               bdisp_h_filter[0].paddr, &attrs);
 197        }
 198}
 199
 200/**
 201 * bdisp_hw_alloc_filters
 202 * @dev:        device
 203 *
 204 * Allocate dma memory for filters
 205 *
 206 * RETURNS:
 207 * 0 on success
 208 */
 209int bdisp_hw_alloc_filters(struct device *dev)
 210{
 211        unsigned int i, size;
 212        void *base;
 213        dma_addr_t paddr;
 214        DEFINE_DMA_ATTRS(attrs);
 215
 216        /* Allocate all the filters within a single memory page */
 217        size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
 218        dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 219        base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs);
 220        if (!base)
 221                return -ENOMEM;
 222
 223        /* Setup filter addresses */
 224        for (i = 0; i < NB_H_FILTER; i++) {
 225                bdisp_h_filter[i].min = bdisp_h_spec[i].min;
 226                bdisp_h_filter[i].max = bdisp_h_spec[i].max;
 227                memcpy(base, bdisp_h_spec[i].coef, BDISP_HF_NB);
 228                bdisp_h_filter[i].virt = base;
 229                bdisp_h_filter[i].paddr = paddr;
 230                base += BDISP_HF_NB;
 231                paddr += BDISP_HF_NB;
 232        }
 233
 234        for (i = 0; i < NB_V_FILTER; i++) {
 235                bdisp_v_filter[i].min = bdisp_v_spec[i].min;
 236                bdisp_v_filter[i].max = bdisp_v_spec[i].max;
 237                memcpy(base, bdisp_v_spec[i].coef, BDISP_VF_NB);
 238                bdisp_v_filter[i].virt = base;
 239                bdisp_v_filter[i].paddr = paddr;
 240                base += BDISP_VF_NB;
 241                paddr += BDISP_VF_NB;
 242        }
 243
 244        return 0;
 245}
 246
 247/**
 248 * bdisp_hw_get_hf_addr
 249 * @inc:        resize increment
 250 *
 251 * Find the horizontal filter table that fits the resize increment
 252 *
 253 * RETURNS:
 254 * table physical address
 255 */
 256static dma_addr_t bdisp_hw_get_hf_addr(u16 inc)
 257{
 258        unsigned int i;
 259
 260        for (i = NB_H_FILTER - 1; i > 0; i--)
 261                if ((bdisp_h_filter[i].min < inc) &&
 262                    (inc <= bdisp_h_filter[i].max))
 263                        break;
 264
 265        return bdisp_h_filter[i].paddr;
 266}
 267
 268/**
 269 * bdisp_hw_get_vf_addr
 270 * @inc:        resize increment
 271 *
 272 * Find the vertical filter table that fits the resize increment
 273 *
 274 * RETURNS:
 275 * table physical address
 276 */
 277static dma_addr_t bdisp_hw_get_vf_addr(u16 inc)
 278{
 279        unsigned int i;
 280
 281        for (i = NB_V_FILTER - 1; i > 0; i--)
 282                if ((bdisp_v_filter[i].min < inc) &&
 283                    (inc <= bdisp_v_filter[i].max))
 284                        break;
 285
 286        return bdisp_v_filter[i].paddr;
 287}
 288
 289/**
 290 * bdisp_hw_get_inc
 291 * @from:       input size
 292 * @to:         output size
 293 * @inc:        resize increment in 6.10 format
 294 *
 295 * Computes the increment (inverse of scale) in 6.10 format
 296 *
 297 * RETURNS:
 298 * 0 on success
 299 */
 300static int bdisp_hw_get_inc(u32 from, u32 to, u16 *inc)
 301{
 302        u32 tmp;
 303
 304        if (!to)
 305                return -EINVAL;
 306
 307        if (to == from) {
 308                *inc = 1 << 10;
 309                return 0;
 310        }
 311
 312        tmp = (from << 10) / to;
 313        if ((tmp > 0xFFFF) || (!tmp))
 314                /* overflow (downscale x 63) or too small (upscale x 1024) */
 315                return -EINVAL;
 316
 317        *inc = (u16)tmp;
 318
 319        return 0;
 320}
 321
 322/**
 323 * bdisp_hw_get_hv_inc
 324 * @ctx:        device context
 325 * @h_inc:      horizontal increment
 326 * @v_inc:      vertical increment
 327 *
 328 * Computes the horizontal & vertical increments (inverse of scale)
 329 *
 330 * RETURNS:
 331 * 0 on success
 332 */
 333static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc)
 334{
 335        u32 src_w, src_h, dst_w, dst_h;
 336
 337        src_w = ctx->src.crop.width;
 338        src_h = ctx->src.crop.height;
 339        dst_w = ctx->dst.width;
 340        dst_h = ctx->dst.height;
 341
 342        if (bdisp_hw_get_inc(src_w, dst_w, h_inc) ||
 343            bdisp_hw_get_inc(src_h, dst_h, v_inc)) {
 344                dev_err(ctx->bdisp_dev->dev,
 345                        "scale factors failed (%dx%d)->(%dx%d)\n",
 346                        src_w, src_h, dst_w, dst_h);
 347                return -EINVAL;
 348        }
 349
 350        return 0;
 351}
 352
 353/**
 354 * bdisp_hw_get_op_cfg
 355 * @ctx:        device context
 356 * @c:          operation configuration
 357 *
 358 * Check which blitter operations are expected and sets the scaling increments
 359 *
 360 * RETURNS:
 361 * 0 on success
 362 */
 363static int bdisp_hw_get_op_cfg(struct bdisp_ctx *ctx, struct bdisp_op_cfg *c)
 364{
 365        struct device *dev = ctx->bdisp_dev->dev;
 366        struct bdisp_frame *src = &ctx->src;
 367        struct bdisp_frame *dst = &ctx->dst;
 368
 369        if (src->width > MAX_SRC_WIDTH * MAX_VERTICAL_STRIDES) {
 370                dev_err(dev, "Image width out of HW caps\n");
 371                return -EINVAL;
 372        }
 373
 374        c->wide = src->width > MAX_SRC_WIDTH;
 375
 376        c->hflip = ctx->hflip;
 377        c->vflip = ctx->vflip;
 378
 379        c->src_interlaced = (src->field == V4L2_FIELD_INTERLACED);
 380
 381        c->src_nbp = src->fmt->nb_planes;
 382        c->src_yuv = (src->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
 383                        (src->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
 384        c->src_420 = c->src_yuv;
 385
 386        c->dst_nbp = dst->fmt->nb_planes;
 387        c->dst_yuv = (dst->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
 388                        (dst->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
 389        c->dst_420 = c->dst_yuv;
 390
 391        c->cconv = (c->src_yuv != c->dst_yuv);
 392
 393        if (bdisp_hw_get_hv_inc(ctx, &c->h_inc, &c->v_inc)) {
 394                dev_err(dev, "Scale factor out of HW caps\n");
 395                return -EINVAL;
 396        }
 397
 398        /* Deinterlacing adjustment : stretch a field to a frame */
 399        if (c->src_interlaced)
 400                c->v_inc /= 2;
 401
 402        if ((c->h_inc != (1 << 10)) || (c->v_inc != (1 << 10)))
 403                c->scale = true;
 404        else
 405                c->scale = false;
 406
 407        return 0;
 408}
 409
 410/**
 411 * bdisp_hw_color_format
 412 * @pixelformat: v4l2 pixel format
 413 *
 414 * v4l2 to bdisp pixel format convert
 415 *
 416 * RETURNS:
 417 * bdisp pixel format
 418 */
 419static u32 bdisp_hw_color_format(u32 pixelformat)
 420{
 421        u32 ret;
 422
 423        switch (pixelformat) {
 424        case V4L2_PIX_FMT_YUV420:
 425                ret = (BDISP_YUV_3B << BLT_TTY_COL_SHIFT);
 426                break;
 427        case V4L2_PIX_FMT_NV12:
 428                ret = (BDISP_NV12 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
 429                break;
 430        case V4L2_PIX_FMT_RGB565:
 431                ret = (BDISP_RGB565 << BLT_TTY_COL_SHIFT);
 432                break;
 433        case V4L2_PIX_FMT_XBGR32: /* This V4L format actually refers to xRGB */
 434                ret = (BDISP_XRGB8888 << BLT_TTY_COL_SHIFT);
 435                break;
 436        case V4L2_PIX_FMT_RGB24:  /* RGB888 format */
 437                ret = (BDISP_RGB888 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
 438                break;
 439        case V4L2_PIX_FMT_ABGR32: /* This V4L format actually refers to ARGB */
 440
 441        default:
 442                ret = (BDISP_ARGB8888 << BLT_TTY_COL_SHIFT) | BLT_TTY_ALPHA_R;
 443                break;
 444        }
 445
 446        return ret;
 447}
 448
 449/**
 450 * bdisp_hw_build_node
 451 * @ctx:        device context
 452 * @cfg:        operation configuration
 453 * @node:       node to be set
 454 * @t_plan:     whether the node refers to a RGB/Y or a CbCr plane
 455 * @src_x_offset: x offset in the source image
 456 *
 457 * Build a node
 458 *
 459 * RETURNS:
 460 * None
 461 */
 462static void bdisp_hw_build_node(struct bdisp_ctx *ctx,
 463                                struct bdisp_op_cfg *cfg,
 464                                struct bdisp_node *node,
 465                                enum bdisp_target_plan t_plan, int src_x_offset)
 466{
 467        struct bdisp_frame *src = &ctx->src;
 468        struct bdisp_frame *dst = &ctx->dst;
 469        u16 h_inc, v_inc, yh_inc, yv_inc;
 470        struct v4l2_rect src_rect = src->crop;
 471        struct v4l2_rect dst_rect = dst->crop;
 472        int dst_x_offset;
 473        s32 dst_width = dst->crop.width;
 474        u32 src_fmt, dst_fmt;
 475        const u32 *ivmx;
 476
 477        dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__);
 478
 479        memset(node, 0, sizeof(*node));
 480
 481        /* Adjust src and dst areas wrt src_x_offset */
 482        src_rect.left += src_x_offset;
 483        src_rect.width -= src_x_offset;
 484        src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width);
 485
 486        dst_x_offset = (src_x_offset * dst->width) / ctx->src.crop.width;
 487        dst_rect.left += dst_x_offset;
 488        dst_rect.width = (src_rect.width * dst->width) / ctx->src.crop.width;
 489
 490        /* General */
 491        src_fmt = src->fmt->pixelformat;
 492        dst_fmt = dst->fmt->pixelformat;
 493
 494        node->nip = 0;
 495        node->cic = BLT_CIC_ALL_GRP;
 496        node->ack = BLT_ACK_BYPASS_S2S3;
 497
 498        switch (cfg->src_nbp) {
 499        case 1:
 500                /* Src2 = RGB / Src1 = Src3 = off */
 501                node->ins = BLT_INS_S1_OFF | BLT_INS_S2_MEM | BLT_INS_S3_OFF;
 502                break;
 503        case 2:
 504                /* Src3 = Y
 505                 * Src2 = CbCr or ColorFill if writing the Y plane
 506                 * Src1 = off */
 507                node->ins = BLT_INS_S1_OFF | BLT_INS_S3_MEM;
 508                if (t_plan == BDISP_Y)
 509                        node->ins |= BLT_INS_S2_CF;
 510                else
 511                        node->ins |= BLT_INS_S2_MEM;
 512                break;
 513        case 3:
 514        default:
 515                /* Src3 = Y
 516                 * Src2 = Cb or ColorFill if writing the Y plane
 517                 * Src1 = Cr or ColorFill if writing the Y plane */
 518                node->ins = BLT_INS_S3_MEM;
 519                if (t_plan == BDISP_Y)
 520                        node->ins |= BLT_INS_S2_CF | BLT_INS_S1_CF;
 521                else
 522                        node->ins |= BLT_INS_S2_MEM | BLT_INS_S1_MEM;
 523                break;
 524        }
 525
 526        /* Color convert */
 527        node->ins |= cfg->cconv ? BLT_INS_IVMX : 0;
 528        /* Scale needed if scaling OR 4:2:0 up/downsampling */
 529        node->ins |= (cfg->scale || cfg->src_420 || cfg->dst_420) ?
 530                        BLT_INS_SCALE : 0;
 531
 532        /* Target */
 533        node->tba = (t_plan == BDISP_CBCR) ? dst->paddr[1] : dst->paddr[0];
 534
 535        node->tty = dst->bytesperline;
 536        node->tty |= bdisp_hw_color_format(dst_fmt);
 537        node->tty |= BLT_TTY_DITHER;
 538        node->tty |= (t_plan == BDISP_CBCR) ? BLT_TTY_CHROMA : 0;
 539        node->tty |= cfg->hflip ? BLT_TTY_HSO : 0;
 540        node->tty |= cfg->vflip ? BLT_TTY_VSO : 0;
 541
 542        if (cfg->dst_420 && (t_plan == BDISP_CBCR)) {
 543                /* 420 chroma downsampling */
 544                dst_rect.height /= 2;
 545                dst_rect.width /= 2;
 546                dst_rect.left /= 2;
 547                dst_rect.top /= 2;
 548                dst_x_offset /= 2;
 549                dst_width /= 2;
 550        }
 551
 552        node->txy = cfg->vflip ? (dst_rect.height - 1) : dst_rect.top;
 553        node->txy <<= 16;
 554        node->txy |= cfg->hflip ? (dst_width - dst_x_offset - 1) :
 555                        dst_rect.left;
 556
 557        node->tsz = dst_rect.height << 16 | dst_rect.width;
 558
 559        if (cfg->src_interlaced) {
 560                /* handle only the top field which is half height of a frame */
 561                src_rect.top /= 2;
 562                src_rect.height /= 2;
 563        }
 564
 565        if (cfg->src_nbp == 1) {
 566                /* Src 2 : RGB */
 567                node->s2ba = src->paddr[0];
 568
 569                node->s2ty = src->bytesperline;
 570                if (cfg->src_interlaced)
 571                        node->s2ty *= 2;
 572
 573                node->s2ty |= bdisp_hw_color_format(src_fmt);
 574
 575                node->s2xy = src_rect.top << 16 | src_rect.left;
 576                node->s2sz = src_rect.height << 16 | src_rect.width;
 577        } else {
 578                /* Src 2 : Cb or CbCr */
 579                if (cfg->src_420) {
 580                        /* 420 chroma upsampling */
 581                        src_rect.top /= 2;
 582                        src_rect.left /= 2;
 583                        src_rect.width /= 2;
 584                        src_rect.height /= 2;
 585                }
 586
 587                node->s2ba = src->paddr[1];
 588
 589                node->s2ty = src->bytesperline;
 590                if (cfg->src_nbp == 3)
 591                        node->s2ty /= 2;
 592                if (cfg->src_interlaced)
 593                        node->s2ty *= 2;
 594
 595                node->s2ty |= bdisp_hw_color_format(src_fmt);
 596
 597                node->s2xy = src_rect.top << 16 | src_rect.left;
 598                node->s2sz = src_rect.height << 16 | src_rect.width;
 599
 600                if (cfg->src_nbp == 3) {
 601                        /* Src 1 : Cr */
 602                        node->s1ba = src->paddr[2];
 603
 604                        node->s1ty = node->s2ty;
 605                        node->s1xy = node->s2xy;
 606                }
 607
 608                /* Src 3 : Y */
 609                node->s3ba = src->paddr[0];
 610
 611                node->s3ty = src->bytesperline;
 612                if (cfg->src_interlaced)
 613                        node->s3ty *= 2;
 614                node->s3ty |= bdisp_hw_color_format(src_fmt);
 615
 616                if ((t_plan != BDISP_CBCR) && cfg->src_420) {
 617                        /* No chroma upsampling for output RGB / Y plane */
 618                        node->s3xy = node->s2xy * 2;
 619                        node->s3sz = node->s2sz * 2;
 620                } else {
 621                        /* No need to read Y (Src3) when writing Chroma */
 622                        node->s3ty |= BLT_S3TY_BLANK_ACC;
 623                        node->s3xy = node->s2xy;
 624                        node->s3sz = node->s2sz;
 625                }
 626        }
 627
 628        /* Resize (scale OR 4:2:0: chroma up/downsampling) */
 629        if (node->ins & BLT_INS_SCALE) {
 630                /* no need to compute Y when writing CbCr from RGB input */
 631                bool skip_y = (t_plan == BDISP_CBCR) && !cfg->src_yuv;
 632
 633                /* FCTL */
 634                if (cfg->scale) {
 635                        node->fctl = BLT_FCTL_HV_SCALE;
 636                        if (!skip_y)
 637                                node->fctl |= BLT_FCTL_Y_HV_SCALE;
 638                } else {
 639                        node->fctl = BLT_FCTL_HV_SAMPLE;
 640                        if (!skip_y)
 641                                node->fctl |= BLT_FCTL_Y_HV_SAMPLE;
 642                }
 643
 644                /* RSF - Chroma may need to be up/downsampled */
 645                h_inc = cfg->h_inc;
 646                v_inc = cfg->v_inc;
 647                if (!cfg->src_420 && cfg->dst_420 && (t_plan == BDISP_CBCR)) {
 648                        /* RGB to 4:2:0 for Chroma: downsample */
 649                        h_inc *= 2;
 650                        v_inc *= 2;
 651                } else if (cfg->src_420 && !cfg->dst_420) {
 652                        /* 4:2:0: to RGB: upsample*/
 653                        h_inc /= 2;
 654                        v_inc /= 2;
 655                }
 656                node->rsf = v_inc << 16 | h_inc;
 657
 658                /* RZI */
 659                node->rzi = BLT_RZI_DEFAULT;
 660
 661                /* Filter table physical addr */
 662                node->hfp = bdisp_hw_get_hf_addr(h_inc);
 663                node->vfp = bdisp_hw_get_vf_addr(v_inc);
 664
 665                /* Y version */
 666                if (!skip_y) {
 667                        yh_inc = cfg->h_inc;
 668                        yv_inc = cfg->v_inc;
 669
 670                        node->y_rsf = yv_inc << 16 | yh_inc;
 671                        node->y_rzi = BLT_RZI_DEFAULT;
 672                        node->y_hfp = bdisp_hw_get_hf_addr(yh_inc);
 673                        node->y_vfp = bdisp_hw_get_vf_addr(yv_inc);
 674                }
 675        }
 676
 677        /* Versatile matrix for RGB / YUV conversion */
 678        if (cfg->cconv) {
 679                ivmx = cfg->src_yuv ? bdisp_yuv_to_rgb : bdisp_rgb_to_yuv;
 680
 681                node->ivmx0 = ivmx[0];
 682                node->ivmx1 = ivmx[1];
 683                node->ivmx2 = ivmx[2];
 684                node->ivmx3 = ivmx[3];
 685        }
 686}
 687
 688/**
 689 * bdisp_hw_build_all_nodes
 690 * @ctx:        device context
 691 *
 692 * Build all the nodes for the blitter operation
 693 *
 694 * RETURNS:
 695 * 0 on success
 696 */
 697static int bdisp_hw_build_all_nodes(struct bdisp_ctx *ctx)
 698{
 699        struct bdisp_op_cfg cfg;
 700        unsigned int i, nid = 0;
 701        int src_x_offset = 0;
 702
 703        for (i = 0; i < MAX_NB_NODE; i++)
 704                if (!ctx->node[i]) {
 705                        dev_err(ctx->bdisp_dev->dev, "node %d is null\n", i);
 706                        return -EINVAL;
 707                }
 708
 709        /* Get configuration (scale, flip, ...) */
 710        if (bdisp_hw_get_op_cfg(ctx, &cfg))
 711                return -EINVAL;
 712
 713        /* Split source in vertical strides (HW constraint) */
 714        for (i = 0; i < MAX_VERTICAL_STRIDES; i++) {
 715                /* Build RGB/Y node and link it to the previous node */
 716                bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
 717                                    cfg.dst_nbp == 1 ? BDISP_RGB : BDISP_Y,
 718                                    src_x_offset);
 719                if (nid)
 720                        ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
 721                nid++;
 722
 723                /* Build additional Cb(Cr) node, link it to the previous one */
 724                if (cfg.dst_nbp > 1) {
 725                        bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
 726                                            BDISP_CBCR, src_x_offset);
 727                        ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
 728                        nid++;
 729                }
 730
 731                /* Next stride until full width covered */
 732                src_x_offset += MAX_SRC_WIDTH;
 733                if (src_x_offset >= ctx->src.crop.width)
 734                        break;
 735        }
 736
 737        /* Mark last node as the last */
 738        ctx->node[nid - 1]->nip = 0;
 739
 740        return 0;
 741}
 742
 743/**
 744 * bdisp_hw_save_request
 745 * @ctx:        device context
 746 *
 747 * Save a copy of the request and of the built nodes
 748 *
 749 * RETURNS:
 750 * None
 751 */
 752static void bdisp_hw_save_request(struct bdisp_ctx *ctx)
 753{
 754        struct bdisp_node **copy_node = ctx->bdisp_dev->dbg.copy_node;
 755        struct bdisp_request *request = &ctx->bdisp_dev->dbg.copy_request;
 756        struct bdisp_node **node = ctx->node;
 757        int i;
 758
 759        /* Request copy */
 760        request->src = ctx->src;
 761        request->dst = ctx->dst;
 762        request->hflip = ctx->hflip;
 763        request->vflip = ctx->vflip;
 764        request->nb_req++;
 765
 766        /* Nodes copy */
 767        for (i = 0; i < MAX_NB_NODE; i++) {
 768                /* Allocate memory if not done yet */
 769                if (!copy_node[i]) {
 770                        copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev,
 771                                                    sizeof(*copy_node),
 772                                                    GFP_KERNEL);
 773                        if (!copy_node[i])
 774                                return;
 775                }
 776                copy_node[i] = node[i];
 777        }
 778}
 779
 780/**
 781 * bdisp_hw_update
 782 * @ctx:        device context
 783 *
 784 * Send the request to the HW
 785 *
 786 * RETURNS:
 787 * 0 on success
 788 */
 789int bdisp_hw_update(struct bdisp_ctx *ctx)
 790{
 791        int ret;
 792        struct bdisp_dev *bdisp = ctx->bdisp_dev;
 793        struct device *dev = bdisp->dev;
 794        unsigned int node_id;
 795
 796        dev_dbg(dev, "%s\n", __func__);
 797
 798        /* build nodes */
 799        ret = bdisp_hw_build_all_nodes(ctx);
 800        if (ret) {
 801                dev_err(dev, "cannot build nodes (%d)\n", ret);
 802                return ret;
 803        }
 804
 805        /* Save a copy of the request */
 806        bdisp_hw_save_request(ctx);
 807
 808        /* Configure interrupt to 'Last Node Reached for AQ1' */
 809        writel(BLT_AQ1_CTL_CFG, bdisp->regs + BLT_AQ1_CTL);
 810        writel(BLT_ITS_AQ1_LNA, bdisp->regs + BLT_ITM0);
 811
 812        /* Write first node addr */
 813        writel(ctx->node_paddr[0], bdisp->regs + BLT_AQ1_IP);
 814
 815        /* Find and write last node addr : this starts the HW processing */
 816        for (node_id = 0; node_id < MAX_NB_NODE - 1; node_id++) {
 817                if (!ctx->node[node_id]->nip)
 818                        break;
 819        }
 820        writel(ctx->node_paddr[node_id], bdisp->regs + BLT_AQ1_LNA);
 821
 822        return 0;
 823}
 824