linux/drivers/media/platform/sti/bdisp/bdisp-hw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) STMicroelectronics SA 2014
   4 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
   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            500
  18#define POLL_RST_DELAY_MS       2
  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 const struct bdisp_filter_h_spec bdisp_h_spec[] = {
  51        {
  52                .min = 0,
  53                .max = 921,
  54                .coef = {
  55                        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  56                        0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00,
  57                        0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00,
  58                        0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00,
  59                        0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00,
  60                        0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00,
  61                        0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00,
  62                        0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00
  63                }
  64        },
  65        {
  66                .min = 921,
  67                .max = 1024,
  68                .coef = {
  69                        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  70                        0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
  71                        0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
  72                        0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
  73                        0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
  74                        0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
  75                        0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
  76                        0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
  77                }
  78        },
  79        {
  80                .min = 1024,
  81                .max = 1126,
  82                .coef = {
  83                        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  84                        0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
  85                        0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
  86                        0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
  87                        0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
  88                        0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
  89                        0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
  90                        0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
  91                }
  92        },
  93        {
  94                .min = 1126,
  95                .max = 1228,
  96                .coef = {
  97                        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  98                        0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
  99                        0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
 100                        0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
 101                        0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
 102                        0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
 103                        0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
 104                        0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
 105                }
 106        },
 107        {
 108                .min = 1228,
 109                .max = 1331,
 110                .coef = {
 111                        0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04,
 112                        0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02,
 113                        0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00,
 114                        0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff,
 115                        0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd,
 116                        0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc,
 117                        0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb,
 118                        0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc
 119                }
 120        },
 121        {
 122                .min = 1331,
 123                .max = 1433,
 124                .coef = {
 125                        0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06,
 126                        0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05,
 127                        0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04,
 128                        0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02,
 129                        0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00,
 130                        0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff,
 131                        0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe,
 132                        0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd
 133                }
 134        },
 135        {
 136                .min = 1433,
 137                .max = 1536,
 138                .coef = {
 139                        0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06,
 140                        0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06,
 141                        0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06,
 142                        0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04,
 143                        0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03,
 144                        0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01,
 145                        0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00,
 146                        0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff
 147                }
 148        },
 149        {
 150                .min = 1536,
 151                .max = 2048,
 152                .coef = {
 153                        0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd,
 154                        0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff,
 155                        0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00,
 156                        0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01,
 157                        0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02,
 158                        0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03,
 159                        0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04,
 160                        0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05
 161                }
 162        },
 163        {
 164                .min = 2048,
 165                .max = 3072,
 166                .coef = {
 167                        0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd,
 168                        0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc,
 169                        0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc,
 170                        0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb,
 171                        0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb,
 172                        0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb,
 173                        0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb,
 174                        0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc
 175                }
 176        },
 177        {
 178                .min = 3072,
 179                .max = 4096,
 180                .coef = {
 181                        0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02,
 182                        0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01,
 183                        0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00,
 184                        0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00,
 185                        0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00,
 186                        0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00,
 187                        0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff,
 188                        0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff
 189                }
 190        },
 191        {
 192                .min = 4096,
 193                .max = 5120,
 194                .coef = {
 195                        0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04,
 196                        0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04,
 197                        0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03,
 198                        0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03,
 199                        0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02,
 200                        0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02,
 201                        0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01,
 202                        0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01
 203                }
 204        },
 205        {
 206                .min = 5120,
 207                .max = 65535,
 208                .coef = {
 209                        0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06,
 210                        0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
 211                        0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
 212                        0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04,
 213                        0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04,
 214                        0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04,
 215                        0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03,
 216                        0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03
 217                }
 218        }
 219};
 220
 221#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec)
 222
 223
 224static const struct bdisp_filter_v_spec bdisp_v_spec[] = {
 225        {
 226                .min = 0,
 227                .max = 1024,
 228                .coef = {
 229                        0x00, 0x00, 0x40, 0x00, 0x00,
 230                        0x00, 0x06, 0x3d, 0xfd, 0x00,
 231                        0xfe, 0x0f, 0x38, 0xfb, 0x00,
 232                        0xfd, 0x19, 0x2f, 0xfb, 0x00,
 233                        0xfc, 0x24, 0x24, 0xfc, 0x00,
 234                        0xfb, 0x2f, 0x19, 0xfd, 0x00,
 235                        0xfb, 0x38, 0x0f, 0xfe, 0x00,
 236                        0xfd, 0x3d, 0x06, 0x00, 0x00
 237                }
 238        },
 239        {
 240                .min = 1024,
 241                .max = 1331,
 242                .coef = {
 243                        0xfc, 0x05, 0x3e, 0x05, 0xfc,
 244                        0xf8, 0x0e, 0x3b, 0xff, 0x00,
 245                        0xf5, 0x18, 0x38, 0xf9, 0x02,
 246                        0xf4, 0x21, 0x31, 0xf5, 0x05,
 247                        0xf4, 0x2a, 0x27, 0xf4, 0x07,
 248                        0xf6, 0x30, 0x1e, 0xf4, 0x08,
 249                        0xf9, 0x35, 0x15, 0xf6, 0x07,
 250                        0xff, 0x37, 0x0b, 0xf9, 0x06
 251                }
 252        },
 253        {
 254                .min = 1331,
 255                .max = 1433,
 256                .coef = {
 257                        0xf8, 0x0a, 0x3c, 0x0a, 0xf8,
 258                        0xf6, 0x12, 0x3b, 0x02, 0xfb,
 259                        0xf4, 0x1b, 0x35, 0xfd, 0xff,
 260                        0xf4, 0x23, 0x30, 0xf8, 0x01,
 261                        0xf6, 0x29, 0x27, 0xf6, 0x04,
 262                        0xf9, 0x2e, 0x1e, 0xf5, 0x06,
 263                        0xfd, 0x31, 0x16, 0xf6, 0x06,
 264                        0x02, 0x32, 0x0d, 0xf8, 0x07
 265                }
 266        },
 267        {
 268                .min = 1433,
 269                .max = 1536,
 270                .coef = {
 271                        0xf6, 0x0e, 0x38, 0x0e, 0xf6,
 272                        0xf5, 0x15, 0x38, 0x06, 0xf8,
 273                        0xf5, 0x1d, 0x33, 0x00, 0xfb,
 274                        0xf6, 0x23, 0x2d, 0xfc, 0xfe,
 275                        0xf9, 0x28, 0x26, 0xf9, 0x00,
 276                        0xfc, 0x2c, 0x1e, 0xf7, 0x03,
 277                        0x00, 0x2e, 0x18, 0xf6, 0x04,
 278                        0x05, 0x2e, 0x11, 0xf7, 0x05
 279                }
 280        },
 281        {
 282                .min = 1536,
 283                .max = 2048,
 284                .coef = {
 285                        0xfb, 0x13, 0x24, 0x13, 0xfb,
 286                        0xfd, 0x17, 0x23, 0x0f, 0xfa,
 287                        0xff, 0x1a, 0x23, 0x0b, 0xf9,
 288                        0x01, 0x1d, 0x22, 0x07, 0xf9,
 289                        0x04, 0x20, 0x1f, 0x04, 0xf9,
 290                        0x07, 0x22, 0x1c, 0x01, 0xfa,
 291                        0x0b, 0x24, 0x17, 0xff, 0xfb,
 292                        0x0f, 0x24, 0x14, 0xfd, 0xfc
 293                }
 294        },
 295        {
 296                .min = 2048,
 297                .max = 3072,
 298                .coef = {
 299                        0x05, 0x10, 0x16, 0x10, 0x05,
 300                        0x06, 0x11, 0x16, 0x0f, 0x04,
 301                        0x08, 0x13, 0x15, 0x0e, 0x02,
 302                        0x09, 0x14, 0x16, 0x0c, 0x01,
 303                        0x0b, 0x15, 0x15, 0x0b, 0x00,
 304                        0x0d, 0x16, 0x13, 0x0a, 0x00,
 305                        0x0f, 0x17, 0x13, 0x08, 0xff,
 306                        0x11, 0x18, 0x12, 0x07, 0xfe
 307                }
 308        },
 309        {
 310                .min = 3072,
 311                .max = 4096,
 312                .coef = {
 313                        0x09, 0x0f, 0x10, 0x0f, 0x09,
 314                        0x09, 0x0f, 0x12, 0x0e, 0x08,
 315                        0x0a, 0x10, 0x11, 0x0e, 0x07,
 316                        0x0b, 0x11, 0x11, 0x0d, 0x06,
 317                        0x0c, 0x11, 0x12, 0x0c, 0x05,
 318                        0x0d, 0x12, 0x11, 0x0c, 0x04,
 319                        0x0e, 0x12, 0x11, 0x0b, 0x04,
 320                        0x0f, 0x13, 0x11, 0x0a, 0x03
 321                }
 322        },
 323        {
 324                .min = 4096,
 325                .max = 5120,
 326                .coef = {
 327                        0x0a, 0x0e, 0x10, 0x0e, 0x0a,
 328                        0x0b, 0x0e, 0x0f, 0x0e, 0x0a,
 329                        0x0b, 0x0f, 0x10, 0x0d, 0x09,
 330                        0x0c, 0x0f, 0x10, 0x0d, 0x08,
 331                        0x0d, 0x0f, 0x0f, 0x0d, 0x08,
 332                        0x0d, 0x10, 0x10, 0x0c, 0x07,
 333                        0x0e, 0x10, 0x0f, 0x0c, 0x07,
 334                        0x0f, 0x10, 0x10, 0x0b, 0x06
 335                }
 336        },
 337        {
 338                .min = 5120,
 339                .max = 65535,
 340                .coef = {
 341                        0x0b, 0x0e, 0x0e, 0x0e, 0x0b,
 342                        0x0b, 0x0e, 0x0f, 0x0d, 0x0b,
 343                        0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
 344                        0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
 345                        0x0d, 0x0f, 0x0e, 0x0d, 0x09,
 346                        0x0d, 0x0f, 0x0f, 0x0c, 0x09,
 347                        0x0e, 0x0f, 0x0e, 0x0c, 0x09,
 348                        0x0e, 0x0f, 0x0f, 0x0c, 0x08
 349                }
 350        }
 351};
 352
 353#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec)
 354
 355static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER];
 356static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER];
 357
 358/**
 359 * bdisp_hw_reset
 360 * @bdisp:      bdisp entity
 361 *
 362 * Resets HW
 363 *
 364 * RETURNS:
 365 * 0 on success.
 366 */
 367int bdisp_hw_reset(struct bdisp_dev *bdisp)
 368{
 369        unsigned int i;
 370
 371        dev_dbg(bdisp->dev, "%s\n", __func__);
 372
 373        /* Mask Interrupt */
 374        writel(0, bdisp->regs + BLT_ITM0);
 375
 376        /* Reset */
 377        writel(readl(bdisp->regs + BLT_CTL) | BLT_CTL_RESET,
 378               bdisp->regs + BLT_CTL);
 379        writel(0, bdisp->regs + BLT_CTL);
 380
 381        /* Wait for reset done */
 382        for (i = 0; i < POLL_RST_MAX; i++) {
 383                if (readl(bdisp->regs + BLT_STA1) & BLT_STA1_IDLE)
 384                        break;
 385                udelay(POLL_RST_DELAY_MS * 1000);
 386        }
 387        if (i == POLL_RST_MAX)
 388                dev_err(bdisp->dev, "Reset timeout\n");
 389
 390        return (i == POLL_RST_MAX) ? -EAGAIN : 0;
 391}
 392
 393/**
 394 * bdisp_hw_get_and_clear_irq
 395 * @bdisp:      bdisp entity
 396 *
 397 * Read then reset interrupt status
 398 *
 399 * RETURNS:
 400 * 0 if expected interrupt was raised.
 401 */
 402int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp)
 403{
 404        u32 its;
 405
 406        its = readl(bdisp->regs + BLT_ITS);
 407
 408        /* Check for the only expected IT: LastNode of AQ1 */
 409        if (!(its & BLT_ITS_AQ1_LNA)) {
 410                dev_dbg(bdisp->dev, "Unexpected IT status: 0x%08X\n", its);
 411                writel(its, bdisp->regs + BLT_ITS);
 412                return -1;
 413        }
 414
 415        /* Clear and mask */
 416        writel(its, bdisp->regs + BLT_ITS);
 417        writel(0, bdisp->regs + BLT_ITM0);
 418
 419        return 0;
 420}
 421
 422/**
 423 * bdisp_hw_free_nodes
 424 * @ctx:        bdisp context
 425 *
 426 * Free node memory
 427 *
 428 * RETURNS:
 429 * None
 430 */
 431void bdisp_hw_free_nodes(struct bdisp_ctx *ctx)
 432{
 433        if (ctx && ctx->node[0])
 434                dma_free_attrs(ctx->bdisp_dev->dev,
 435                               sizeof(struct bdisp_node) * MAX_NB_NODE,
 436                               ctx->node[0], ctx->node_paddr[0],
 437                               DMA_ATTR_WRITE_COMBINE);
 438}
 439
 440/**
 441 * bdisp_hw_alloc_nodes
 442 * @ctx:        bdisp context
 443 *
 444 * Allocate dma memory for nodes
 445 *
 446 * RETURNS:
 447 * 0 on success
 448 */
 449int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx)
 450{
 451        struct device *dev = ctx->bdisp_dev->dev;
 452        unsigned int i, node_size = sizeof(struct bdisp_node);
 453        void *base;
 454        dma_addr_t paddr;
 455
 456        /* Allocate all the nodes within a single memory page */
 457        base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr,
 458                               GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
 459        if (!base) {
 460                dev_err(dev, "%s no mem\n", __func__);
 461                return -ENOMEM;
 462        }
 463
 464        memset(base, 0, node_size * MAX_NB_NODE);
 465
 466        for (i = 0; i < MAX_NB_NODE; i++) {
 467                ctx->node[i] = base;
 468                ctx->node_paddr[i] = paddr;
 469                dev_dbg(dev, "node[%d]=0x%p (paddr=%pad)\n", i, ctx->node[i],
 470                        &paddr);
 471                base += node_size;
 472                paddr += node_size;
 473        }
 474
 475        return 0;
 476}
 477
 478/**
 479 * bdisp_hw_free_filters
 480 * @dev:        device
 481 *
 482 * Free filters memory
 483 *
 484 * RETURNS:
 485 * None
 486 */
 487void bdisp_hw_free_filters(struct device *dev)
 488{
 489        int size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
 490
 491        if (bdisp_h_filter[0].virt)
 492                dma_free_attrs(dev, size, bdisp_h_filter[0].virt,
 493                               bdisp_h_filter[0].paddr, DMA_ATTR_WRITE_COMBINE);
 494}
 495
 496/**
 497 * bdisp_hw_alloc_filters
 498 * @dev:        device
 499 *
 500 * Allocate dma memory for filters
 501 *
 502 * RETURNS:
 503 * 0 on success
 504 */
 505int bdisp_hw_alloc_filters(struct device *dev)
 506{
 507        unsigned int i, size;
 508        void *base;
 509        dma_addr_t paddr;
 510
 511        /* Allocate all the filters within a single memory page */
 512        size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
 513        base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL,
 514                               DMA_ATTR_WRITE_COMBINE);
 515        if (!base)
 516                return -ENOMEM;
 517
 518        /* Setup filter addresses */
 519        for (i = 0; i < NB_H_FILTER; i++) {
 520                bdisp_h_filter[i].min = bdisp_h_spec[i].min;
 521                bdisp_h_filter[i].max = bdisp_h_spec[i].max;
 522                memcpy(base, bdisp_h_spec[i].coef, BDISP_HF_NB);
 523                bdisp_h_filter[i].virt = base;
 524                bdisp_h_filter[i].paddr = paddr;
 525                base += BDISP_HF_NB;
 526                paddr += BDISP_HF_NB;
 527        }
 528
 529        for (i = 0; i < NB_V_FILTER; i++) {
 530                bdisp_v_filter[i].min = bdisp_v_spec[i].min;
 531                bdisp_v_filter[i].max = bdisp_v_spec[i].max;
 532                memcpy(base, bdisp_v_spec[i].coef, BDISP_VF_NB);
 533                bdisp_v_filter[i].virt = base;
 534                bdisp_v_filter[i].paddr = paddr;
 535                base += BDISP_VF_NB;
 536                paddr += BDISP_VF_NB;
 537        }
 538
 539        return 0;
 540}
 541
 542/**
 543 * bdisp_hw_get_hf_addr
 544 * @inc:        resize increment
 545 *
 546 * Find the horizontal filter table that fits the resize increment
 547 *
 548 * RETURNS:
 549 * table physical address
 550 */
 551static dma_addr_t bdisp_hw_get_hf_addr(u16 inc)
 552{
 553        unsigned int i;
 554
 555        for (i = NB_H_FILTER - 1; i > 0; i--)
 556                if ((bdisp_h_filter[i].min < inc) &&
 557                    (inc <= bdisp_h_filter[i].max))
 558                        break;
 559
 560        return bdisp_h_filter[i].paddr;
 561}
 562
 563/**
 564 * bdisp_hw_get_vf_addr
 565 * @inc:        resize increment
 566 *
 567 * Find the vertical filter table that fits the resize increment
 568 *
 569 * RETURNS:
 570 * table physical address
 571 */
 572static dma_addr_t bdisp_hw_get_vf_addr(u16 inc)
 573{
 574        unsigned int i;
 575
 576        for (i = NB_V_FILTER - 1; i > 0; i--)
 577                if ((bdisp_v_filter[i].min < inc) &&
 578                    (inc <= bdisp_v_filter[i].max))
 579                        break;
 580
 581        return bdisp_v_filter[i].paddr;
 582}
 583
 584/**
 585 * bdisp_hw_get_inc
 586 * @from:       input size
 587 * @to:         output size
 588 * @inc:        resize increment in 6.10 format
 589 *
 590 * Computes the increment (inverse of scale) in 6.10 format
 591 *
 592 * RETURNS:
 593 * 0 on success
 594 */
 595static int bdisp_hw_get_inc(u32 from, u32 to, u16 *inc)
 596{
 597        u32 tmp;
 598
 599        if (!to)
 600                return -EINVAL;
 601
 602        if (to == from) {
 603                *inc = 1 << 10;
 604                return 0;
 605        }
 606
 607        tmp = (from << 10) / to;
 608        if ((tmp > 0xFFFF) || (!tmp))
 609                /* overflow (downscale x 63) or too small (upscale x 1024) */
 610                return -EINVAL;
 611
 612        *inc = (u16)tmp;
 613
 614        return 0;
 615}
 616
 617/**
 618 * bdisp_hw_get_hv_inc
 619 * @ctx:        device context
 620 * @h_inc:      horizontal increment
 621 * @v_inc:      vertical increment
 622 *
 623 * Computes the horizontal & vertical increments (inverse of scale)
 624 *
 625 * RETURNS:
 626 * 0 on success
 627 */
 628static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc)
 629{
 630        u32 src_w, src_h, dst_w, dst_h;
 631
 632        src_w = ctx->src.crop.width;
 633        src_h = ctx->src.crop.height;
 634        dst_w = ctx->dst.crop.width;
 635        dst_h = ctx->dst.crop.height;
 636
 637        if (bdisp_hw_get_inc(src_w, dst_w, h_inc) ||
 638            bdisp_hw_get_inc(src_h, dst_h, v_inc)) {
 639                dev_err(ctx->bdisp_dev->dev,
 640                        "scale factors failed (%dx%d)->(%dx%d)\n",
 641                        src_w, src_h, dst_w, dst_h);
 642                return -EINVAL;
 643        }
 644
 645        return 0;
 646}
 647
 648/**
 649 * bdisp_hw_get_op_cfg
 650 * @ctx:        device context
 651 * @c:          operation configuration
 652 *
 653 * Check which blitter operations are expected and sets the scaling increments
 654 *
 655 * RETURNS:
 656 * 0 on success
 657 */
 658static int bdisp_hw_get_op_cfg(struct bdisp_ctx *ctx, struct bdisp_op_cfg *c)
 659{
 660        struct device *dev = ctx->bdisp_dev->dev;
 661        struct bdisp_frame *src = &ctx->src;
 662        struct bdisp_frame *dst = &ctx->dst;
 663
 664        if (src->width > MAX_SRC_WIDTH * MAX_VERTICAL_STRIDES) {
 665                dev_err(dev, "Image width out of HW caps\n");
 666                return -EINVAL;
 667        }
 668
 669        c->wide = src->width > MAX_SRC_WIDTH;
 670
 671        c->hflip = ctx->hflip;
 672        c->vflip = ctx->vflip;
 673
 674        c->src_interlaced = (src->field == V4L2_FIELD_INTERLACED);
 675
 676        c->src_nbp = src->fmt->nb_planes;
 677        c->src_yuv = (src->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
 678                        (src->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
 679        c->src_420 = c->src_yuv;
 680
 681        c->dst_nbp = dst->fmt->nb_planes;
 682        c->dst_yuv = (dst->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
 683                        (dst->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
 684        c->dst_420 = c->dst_yuv;
 685
 686        c->cconv = (c->src_yuv != c->dst_yuv);
 687
 688        if (bdisp_hw_get_hv_inc(ctx, &c->h_inc, &c->v_inc)) {
 689                dev_err(dev, "Scale factor out of HW caps\n");
 690                return -EINVAL;
 691        }
 692
 693        /* Deinterlacing adjustment : stretch a field to a frame */
 694        if (c->src_interlaced)
 695                c->v_inc /= 2;
 696
 697        if ((c->h_inc != (1 << 10)) || (c->v_inc != (1 << 10)))
 698                c->scale = true;
 699        else
 700                c->scale = false;
 701
 702        return 0;
 703}
 704
 705/**
 706 * bdisp_hw_color_format
 707 * @pixelformat: v4l2 pixel format
 708 *
 709 * v4l2 to bdisp pixel format convert
 710 *
 711 * RETURNS:
 712 * bdisp pixel format
 713 */
 714static u32 bdisp_hw_color_format(u32 pixelformat)
 715{
 716        u32 ret;
 717
 718        switch (pixelformat) {
 719        case V4L2_PIX_FMT_YUV420:
 720                ret = (BDISP_YUV_3B << BLT_TTY_COL_SHIFT);
 721                break;
 722        case V4L2_PIX_FMT_NV12:
 723                ret = (BDISP_NV12 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
 724                break;
 725        case V4L2_PIX_FMT_RGB565:
 726                ret = (BDISP_RGB565 << BLT_TTY_COL_SHIFT);
 727                break;
 728        case V4L2_PIX_FMT_XBGR32: /* This V4L format actually refers to xRGB */
 729                ret = (BDISP_XRGB8888 << BLT_TTY_COL_SHIFT);
 730                break;
 731        case V4L2_PIX_FMT_RGB24:  /* RGB888 format */
 732                ret = (BDISP_RGB888 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
 733                break;
 734        case V4L2_PIX_FMT_ABGR32: /* This V4L format actually refers to ARGB */
 735
 736        default:
 737                ret = (BDISP_ARGB8888 << BLT_TTY_COL_SHIFT) | BLT_TTY_ALPHA_R;
 738                break;
 739        }
 740
 741        return ret;
 742}
 743
 744/**
 745 * bdisp_hw_build_node
 746 * @ctx:        device context
 747 * @cfg:        operation configuration
 748 * @node:       node to be set
 749 * @t_plan:     whether the node refers to a RGB/Y or a CbCr plane
 750 * @src_x_offset: x offset in the source image
 751 *
 752 * Build a node
 753 *
 754 * RETURNS:
 755 * None
 756 */
 757static void bdisp_hw_build_node(struct bdisp_ctx *ctx,
 758                                struct bdisp_op_cfg *cfg,
 759                                struct bdisp_node *node,
 760                                enum bdisp_target_plan t_plan, int src_x_offset)
 761{
 762        struct bdisp_frame *src = &ctx->src;
 763        struct bdisp_frame *dst = &ctx->dst;
 764        u16 h_inc, v_inc, yh_inc, yv_inc;
 765        struct v4l2_rect src_rect = src->crop;
 766        struct v4l2_rect dst_rect = dst->crop;
 767        int dst_x_offset;
 768        s32 dst_width = dst->crop.width;
 769        u32 src_fmt, dst_fmt;
 770        const u32 *ivmx;
 771
 772        dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__);
 773
 774        memset(node, 0, sizeof(*node));
 775
 776        /* Adjust src and dst areas wrt src_x_offset */
 777        src_rect.left += src_x_offset;
 778        src_rect.width -= src_x_offset;
 779        src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width);
 780
 781        dst_x_offset = (src_x_offset * dst_width) / ctx->src.crop.width;
 782        dst_rect.left += dst_x_offset;
 783        dst_rect.width = (src_rect.width * dst_width) / ctx->src.crop.width;
 784
 785        /* General */
 786        src_fmt = src->fmt->pixelformat;
 787        dst_fmt = dst->fmt->pixelformat;
 788
 789        node->nip = 0;
 790        node->cic = BLT_CIC_ALL_GRP;
 791        node->ack = BLT_ACK_BYPASS_S2S3;
 792
 793        switch (cfg->src_nbp) {
 794        case 1:
 795                /* Src2 = RGB / Src1 = Src3 = off */
 796                node->ins = BLT_INS_S1_OFF | BLT_INS_S2_MEM | BLT_INS_S3_OFF;
 797                break;
 798        case 2:
 799                /* Src3 = Y
 800                 * Src2 = CbCr or ColorFill if writing the Y plane
 801                 * Src1 = off */
 802                node->ins = BLT_INS_S1_OFF | BLT_INS_S3_MEM;
 803                if (t_plan == BDISP_Y)
 804                        node->ins |= BLT_INS_S2_CF;
 805                else
 806                        node->ins |= BLT_INS_S2_MEM;
 807                break;
 808        case 3:
 809        default:
 810                /* Src3 = Y
 811                 * Src2 = Cb or ColorFill if writing the Y plane
 812                 * Src1 = Cr or ColorFill if writing the Y plane */
 813                node->ins = BLT_INS_S3_MEM;
 814                if (t_plan == BDISP_Y)
 815                        node->ins |= BLT_INS_S2_CF | BLT_INS_S1_CF;
 816                else
 817                        node->ins |= BLT_INS_S2_MEM | BLT_INS_S1_MEM;
 818                break;
 819        }
 820
 821        /* Color convert */
 822        node->ins |= cfg->cconv ? BLT_INS_IVMX : 0;
 823        /* Scale needed if scaling OR 4:2:0 up/downsampling */
 824        node->ins |= (cfg->scale || cfg->src_420 || cfg->dst_420) ?
 825                        BLT_INS_SCALE : 0;
 826
 827        /* Target */
 828        node->tba = (t_plan == BDISP_CBCR) ? dst->paddr[1] : dst->paddr[0];
 829
 830        node->tty = dst->bytesperline;
 831        node->tty |= bdisp_hw_color_format(dst_fmt);
 832        node->tty |= BLT_TTY_DITHER;
 833        node->tty |= (t_plan == BDISP_CBCR) ? BLT_TTY_CHROMA : 0;
 834        node->tty |= cfg->hflip ? BLT_TTY_HSO : 0;
 835        node->tty |= cfg->vflip ? BLT_TTY_VSO : 0;
 836
 837        if (cfg->dst_420 && (t_plan == BDISP_CBCR)) {
 838                /* 420 chroma downsampling */
 839                dst_rect.height /= 2;
 840                dst_rect.width /= 2;
 841                dst_rect.left /= 2;
 842                dst_rect.top /= 2;
 843                dst_x_offset /= 2;
 844                dst_width /= 2;
 845        }
 846
 847        node->txy = cfg->vflip ? (dst_rect.height - 1) : dst_rect.top;
 848        node->txy <<= 16;
 849        node->txy |= cfg->hflip ? (dst_width - dst_x_offset - 1) :
 850                        dst_rect.left;
 851
 852        node->tsz = dst_rect.height << 16 | dst_rect.width;
 853
 854        if (cfg->src_interlaced) {
 855                /* handle only the top field which is half height of a frame */
 856                src_rect.top /= 2;
 857                src_rect.height /= 2;
 858        }
 859
 860        if (cfg->src_nbp == 1) {
 861                /* Src 2 : RGB */
 862                node->s2ba = src->paddr[0];
 863
 864                node->s2ty = src->bytesperline;
 865                if (cfg->src_interlaced)
 866                        node->s2ty *= 2;
 867
 868                node->s2ty |= bdisp_hw_color_format(src_fmt);
 869
 870                node->s2xy = src_rect.top << 16 | src_rect.left;
 871                node->s2sz = src_rect.height << 16 | src_rect.width;
 872        } else {
 873                /* Src 2 : Cb or CbCr */
 874                if (cfg->src_420) {
 875                        /* 420 chroma upsampling */
 876                        src_rect.top /= 2;
 877                        src_rect.left /= 2;
 878                        src_rect.width /= 2;
 879                        src_rect.height /= 2;
 880                }
 881
 882                node->s2ba = src->paddr[1];
 883
 884                node->s2ty = src->bytesperline;
 885                if (cfg->src_nbp == 3)
 886                        node->s2ty /= 2;
 887                if (cfg->src_interlaced)
 888                        node->s2ty *= 2;
 889
 890                node->s2ty |= bdisp_hw_color_format(src_fmt);
 891
 892                node->s2xy = src_rect.top << 16 | src_rect.left;
 893                node->s2sz = src_rect.height << 16 | src_rect.width;
 894
 895                if (cfg->src_nbp == 3) {
 896                        /* Src 1 : Cr */
 897                        node->s1ba = src->paddr[2];
 898
 899                        node->s1ty = node->s2ty;
 900                        node->s1xy = node->s2xy;
 901                }
 902
 903                /* Src 3 : Y */
 904                node->s3ba = src->paddr[0];
 905
 906                node->s3ty = src->bytesperline;
 907                if (cfg->src_interlaced)
 908                        node->s3ty *= 2;
 909                node->s3ty |= bdisp_hw_color_format(src_fmt);
 910
 911                if ((t_plan != BDISP_CBCR) && cfg->src_420) {
 912                        /* No chroma upsampling for output RGB / Y plane */
 913                        node->s3xy = node->s2xy * 2;
 914                        node->s3sz = node->s2sz * 2;
 915                } else {
 916                        /* No need to read Y (Src3) when writing Chroma */
 917                        node->s3ty |= BLT_S3TY_BLANK_ACC;
 918                        node->s3xy = node->s2xy;
 919                        node->s3sz = node->s2sz;
 920                }
 921        }
 922
 923        /* Resize (scale OR 4:2:0: chroma up/downsampling) */
 924        if (node->ins & BLT_INS_SCALE) {
 925                /* no need to compute Y when writing CbCr from RGB input */
 926                bool skip_y = (t_plan == BDISP_CBCR) && !cfg->src_yuv;
 927
 928                /* FCTL */
 929                if (cfg->scale) {
 930                        node->fctl = BLT_FCTL_HV_SCALE;
 931                        if (!skip_y)
 932                                node->fctl |= BLT_FCTL_Y_HV_SCALE;
 933                } else {
 934                        node->fctl = BLT_FCTL_HV_SAMPLE;
 935                        if (!skip_y)
 936                                node->fctl |= BLT_FCTL_Y_HV_SAMPLE;
 937                }
 938
 939                /* RSF - Chroma may need to be up/downsampled */
 940                h_inc = cfg->h_inc;
 941                v_inc = cfg->v_inc;
 942                if (!cfg->src_420 && cfg->dst_420 && (t_plan == BDISP_CBCR)) {
 943                        /* RGB to 4:2:0 for Chroma: downsample */
 944                        h_inc *= 2;
 945                        v_inc *= 2;
 946                } else if (cfg->src_420 && !cfg->dst_420) {
 947                        /* 4:2:0: to RGB: upsample*/
 948                        h_inc /= 2;
 949                        v_inc /= 2;
 950                }
 951                node->rsf = v_inc << 16 | h_inc;
 952
 953                /* RZI */
 954                node->rzi = BLT_RZI_DEFAULT;
 955
 956                /* Filter table physical addr */
 957                node->hfp = bdisp_hw_get_hf_addr(h_inc);
 958                node->vfp = bdisp_hw_get_vf_addr(v_inc);
 959
 960                /* Y version */
 961                if (!skip_y) {
 962                        yh_inc = cfg->h_inc;
 963                        yv_inc = cfg->v_inc;
 964
 965                        node->y_rsf = yv_inc << 16 | yh_inc;
 966                        node->y_rzi = BLT_RZI_DEFAULT;
 967                        node->y_hfp = bdisp_hw_get_hf_addr(yh_inc);
 968                        node->y_vfp = bdisp_hw_get_vf_addr(yv_inc);
 969                }
 970        }
 971
 972        /* Versatile matrix for RGB / YUV conversion */
 973        if (cfg->cconv) {
 974                ivmx = cfg->src_yuv ? bdisp_yuv_to_rgb : bdisp_rgb_to_yuv;
 975
 976                node->ivmx0 = ivmx[0];
 977                node->ivmx1 = ivmx[1];
 978                node->ivmx2 = ivmx[2];
 979                node->ivmx3 = ivmx[3];
 980        }
 981}
 982
 983/**
 984 * bdisp_hw_build_all_nodes
 985 * @ctx:        device context
 986 *
 987 * Build all the nodes for the blitter operation
 988 *
 989 * RETURNS:
 990 * 0 on success
 991 */
 992static int bdisp_hw_build_all_nodes(struct bdisp_ctx *ctx)
 993{
 994        struct bdisp_op_cfg cfg;
 995        unsigned int i, nid = 0;
 996        int src_x_offset = 0;
 997
 998        for (i = 0; i < MAX_NB_NODE; i++)
 999                if (!ctx->node[i]) {
1000                        dev_err(ctx->bdisp_dev->dev, "node %d is null\n", i);
1001                        return -EINVAL;
1002                }
1003
1004        /* Get configuration (scale, flip, ...) */
1005        if (bdisp_hw_get_op_cfg(ctx, &cfg))
1006                return -EINVAL;
1007
1008        /* Split source in vertical strides (HW constraint) */
1009        for (i = 0; i < MAX_VERTICAL_STRIDES; i++) {
1010                /* Build RGB/Y node and link it to the previous node */
1011                bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
1012                                    cfg.dst_nbp == 1 ? BDISP_RGB : BDISP_Y,
1013                                    src_x_offset);
1014                if (nid)
1015                        ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
1016                nid++;
1017
1018                /* Build additional Cb(Cr) node, link it to the previous one */
1019                if (cfg.dst_nbp > 1) {
1020                        bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
1021                                            BDISP_CBCR, src_x_offset);
1022                        ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
1023                        nid++;
1024                }
1025
1026                /* Next stride until full width covered */
1027                src_x_offset += MAX_SRC_WIDTH;
1028                if (src_x_offset >= ctx->src.crop.width)
1029                        break;
1030        }
1031
1032        /* Mark last node as the last */
1033        ctx->node[nid - 1]->nip = 0;
1034
1035        return 0;
1036}
1037
1038/**
1039 * bdisp_hw_save_request
1040 * @ctx:        device context
1041 *
1042 * Save a copy of the request and of the built nodes
1043 *
1044 * RETURNS:
1045 * None
1046 */
1047static void bdisp_hw_save_request(struct bdisp_ctx *ctx)
1048{
1049        struct bdisp_node **copy_node = ctx->bdisp_dev->dbg.copy_node;
1050        struct bdisp_request *request = &ctx->bdisp_dev->dbg.copy_request;
1051        struct bdisp_node **node = ctx->node;
1052        int i;
1053
1054        /* Request copy */
1055        request->src = ctx->src;
1056        request->dst = ctx->dst;
1057        request->hflip = ctx->hflip;
1058        request->vflip = ctx->vflip;
1059        request->nb_req++;
1060
1061        /* Nodes copy */
1062        for (i = 0; i < MAX_NB_NODE; i++) {
1063                /* Allocate memory if not done yet */
1064                if (!copy_node[i]) {
1065                        copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev,
1066                                                    sizeof(*copy_node[i]),
1067                                                    GFP_ATOMIC);
1068                        if (!copy_node[i])
1069                                return;
1070                }
1071                *copy_node[i] = *node[i];
1072        }
1073}
1074
1075/**
1076 * bdisp_hw_update
1077 * @ctx:        device context
1078 *
1079 * Send the request to the HW
1080 *
1081 * RETURNS:
1082 * 0 on success
1083 */
1084int bdisp_hw_update(struct bdisp_ctx *ctx)
1085{
1086        int ret;
1087        struct bdisp_dev *bdisp = ctx->bdisp_dev;
1088        struct device *dev = bdisp->dev;
1089        unsigned int node_id;
1090
1091        dev_dbg(dev, "%s\n", __func__);
1092
1093        /* build nodes */
1094        ret = bdisp_hw_build_all_nodes(ctx);
1095        if (ret) {
1096                dev_err(dev, "cannot build nodes (%d)\n", ret);
1097                return ret;
1098        }
1099
1100        /* Save a copy of the request */
1101        bdisp_hw_save_request(ctx);
1102
1103        /* Configure interrupt to 'Last Node Reached for AQ1' */
1104        writel(BLT_AQ1_CTL_CFG, bdisp->regs + BLT_AQ1_CTL);
1105        writel(BLT_ITS_AQ1_LNA, bdisp->regs + BLT_ITM0);
1106
1107        /* Write first node addr */
1108        writel(ctx->node_paddr[0], bdisp->regs + BLT_AQ1_IP);
1109
1110        /* Find and write last node addr : this starts the HW processing */
1111        for (node_id = 0; node_id < MAX_NB_NODE - 1; node_id++) {
1112                if (!ctx->node[node_id]->nip)
1113                        break;
1114        }
1115        writel(ctx->node_paddr[node_id], bdisp->regs + BLT_AQ1_LNA);
1116
1117        return 0;
1118}
1119