cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bdisp-hw.c (27932B)


      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}