Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] bdisp: 2D blitter driver using v4l2 mem2mem framework

This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
It uses the v4l2 mem2mem framework.

The following features are supported and tested:
- Color format conversion (RGB32, RGB24, RGB16, NV12, YUV420P)
- Copy
- Scale
- Flip
- Deinterlace
- Wide (4K) picture support
- Crop

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
[hans.verkuil@cisco.com: added missing slab.h include to bdisp-v4l2.c]

Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

authored by

Fabien Dessenne and committed by
Mauro Carvalho Chehab
28ffeebb 5a54cd2a

+2979
+10
drivers/media/platform/Kconfig
··· 212 212 help 213 213 This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. 214 214 215 + config VIDEO_STI_BDISP 216 + tristate "STMicroelectronics BDISP 2D blitter driver" 217 + depends on VIDEO_DEV && VIDEO_V4L2 218 + depends on ARCH_STI || COMPILE_TEST 219 + depends on HAS_DMA 220 + select VIDEOBUF2_DMA_CONTIG 221 + select V4L2_MEM2MEM_DEV 222 + help 223 + This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. 224 + 215 225 config VIDEO_SH_VEU 216 226 tristate "SuperH VEU mem2mem video processing driver" 217 227 depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+2
drivers/media/platform/Makefile
··· 34 34 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ 35 35 obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ 36 36 37 + obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ 38 + 37 39 obj-$(CONFIG_BLACKFIN) += blackfin/ 38 40 39 41 obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+9
drivers/media/platform/sti/bdisp/Kconfig
··· 1 + config VIDEO_STI_BDISP 2 + tristate "STMicroelectronics BDISP 2D blitter driver" 3 + depends on VIDEO_DEV && VIDEO_V4L2 4 + select VIDEOBUF2_DMA_CONTIG 5 + select V4L2_MEM2MEM_DEV 6 + help 7 + This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. 8 + To compile this driver as a module, choose M here: the module will 9 + be called bdisp.ko.
+3
drivers/media/platform/sti/bdisp/Makefile
··· 1 + obj-$(CONFIG_VIDEO_STI_BDISP) := bdisp.o 2 + 3 + bdisp-objs := bdisp-v4l2.o bdisp-hw.o
+346
drivers/media/platform/sti/bdisp/bdisp-filter.h
··· 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 + #define BDISP_HF_NB 64 8 + #define BDISP_VF_NB 40 9 + 10 + /** 11 + * struct bdisp_filter_h_spec - Horizontal filter specification 12 + * 13 + * @min: min scale factor for this filter (6.10 fixed point) 14 + * @max: max scale factor for this filter (6.10 fixed point) 15 + * coef: filter coefficients 16 + */ 17 + struct bdisp_filter_h_spec { 18 + const u16 min; 19 + const u16 max; 20 + const u8 coef[BDISP_HF_NB]; 21 + }; 22 + 23 + static const struct bdisp_filter_h_spec bdisp_h_spec[] = { 24 + { 25 + .min = 0, 26 + .max = 921, 27 + .coef = { 28 + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 29 + 0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00, 30 + 0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00, 31 + 0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00, 32 + 0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00, 33 + 0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00, 34 + 0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00, 35 + 0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00 36 + } 37 + }, 38 + { 39 + .min = 921, 40 + .max = 1024, 41 + .coef = { 42 + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 43 + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, 44 + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, 45 + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, 46 + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, 47 + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, 48 + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, 49 + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff 50 + } 51 + }, 52 + { 53 + .min = 1024, 54 + .max = 1126, 55 + .coef = { 56 + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 57 + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, 58 + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, 59 + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, 60 + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, 61 + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, 62 + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, 63 + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff 64 + } 65 + }, 66 + { 67 + .min = 1126, 68 + .max = 1228, 69 + .coef = { 70 + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 71 + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, 72 + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, 73 + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, 74 + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, 75 + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, 76 + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, 77 + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff 78 + } 79 + }, 80 + { 81 + .min = 1228, 82 + .max = 1331, 83 + .coef = { 84 + 0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04, 85 + 0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02, 86 + 0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00, 87 + 0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff, 88 + 0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd, 89 + 0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc, 90 + 0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb, 91 + 0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc 92 + } 93 + }, 94 + { 95 + .min = 1331, 96 + .max = 1433, 97 + .coef = { 98 + 0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06, 99 + 0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05, 100 + 0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04, 101 + 0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02, 102 + 0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00, 103 + 0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff, 104 + 0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe, 105 + 0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd 106 + } 107 + }, 108 + { 109 + .min = 1433, 110 + .max = 1536, 111 + .coef = { 112 + 0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06, 113 + 0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06, 114 + 0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06, 115 + 0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04, 116 + 0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03, 117 + 0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01, 118 + 0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00, 119 + 0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff 120 + } 121 + }, 122 + { 123 + .min = 1536, 124 + .max = 2048, 125 + .coef = { 126 + 0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd, 127 + 0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff, 128 + 0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00, 129 + 0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01, 130 + 0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02, 131 + 0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03, 132 + 0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04, 133 + 0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05 134 + } 135 + }, 136 + { 137 + .min = 2048, 138 + .max = 3072, 139 + .coef = { 140 + 0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd, 141 + 0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc, 142 + 0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc, 143 + 0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb, 144 + 0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb, 145 + 0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb, 146 + 0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb, 147 + 0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc 148 + } 149 + }, 150 + { 151 + .min = 3072, 152 + .max = 4096, 153 + .coef = { 154 + 0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02, 155 + 0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01, 156 + 0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00, 157 + 0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00, 158 + 0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00, 159 + 0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00, 160 + 0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff, 161 + 0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff 162 + } 163 + }, 164 + { 165 + .min = 4096, 166 + .max = 5120, 167 + .coef = { 168 + 0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04, 169 + 0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04, 170 + 0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03, 171 + 0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03, 172 + 0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02, 173 + 0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02, 174 + 0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01, 175 + 0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01 176 + } 177 + }, 178 + { 179 + .min = 5120, 180 + .max = 65535, 181 + .coef = { 182 + 0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06, 183 + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, 184 + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, 185 + 0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04, 186 + 0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04, 187 + 0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04, 188 + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03, 189 + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03 190 + } 191 + } 192 + }; 193 + 194 + /** 195 + * struct bdisp_filter_v_spec - Vertical filter specification 196 + * 197 + * @min: min scale factor for this filter (6.10 fixed point) 198 + * @max: max scale factor for this filter (6.10 fixed point) 199 + * coef: filter coefficients 200 + */ 201 + struct bdisp_filter_v_spec { 202 + const u16 min; 203 + const u16 max; 204 + const u8 coef[BDISP_VF_NB]; 205 + }; 206 + 207 + static const struct bdisp_filter_v_spec bdisp_v_spec[] = { 208 + { 209 + .min = 0, 210 + .max = 1024, 211 + .coef = { 212 + 0x00, 0x00, 0x40, 0x00, 0x00, 213 + 0x00, 0x06, 0x3d, 0xfd, 0x00, 214 + 0xfe, 0x0f, 0x38, 0xfb, 0x00, 215 + 0xfd, 0x19, 0x2f, 0xfb, 0x00, 216 + 0xfc, 0x24, 0x24, 0xfc, 0x00, 217 + 0xfb, 0x2f, 0x19, 0xfd, 0x00, 218 + 0xfb, 0x38, 0x0f, 0xfe, 0x00, 219 + 0xfd, 0x3d, 0x06, 0x00, 0x00 220 + } 221 + }, 222 + { 223 + .min = 1024, 224 + .max = 1331, 225 + .coef = { 226 + 0xfc, 0x05, 0x3e, 0x05, 0xfc, 227 + 0xf8, 0x0e, 0x3b, 0xff, 0x00, 228 + 0xf5, 0x18, 0x38, 0xf9, 0x02, 229 + 0xf4, 0x21, 0x31, 0xf5, 0x05, 230 + 0xf4, 0x2a, 0x27, 0xf4, 0x07, 231 + 0xf6, 0x30, 0x1e, 0xf4, 0x08, 232 + 0xf9, 0x35, 0x15, 0xf6, 0x07, 233 + 0xff, 0x37, 0x0b, 0xf9, 0x06 234 + } 235 + }, 236 + { 237 + .min = 1331, 238 + .max = 1433, 239 + .coef = { 240 + 0xf8, 0x0a, 0x3c, 0x0a, 0xf8, 241 + 0xf6, 0x12, 0x3b, 0x02, 0xfb, 242 + 0xf4, 0x1b, 0x35, 0xfd, 0xff, 243 + 0xf4, 0x23, 0x30, 0xf8, 0x01, 244 + 0xf6, 0x29, 0x27, 0xf6, 0x04, 245 + 0xf9, 0x2e, 0x1e, 0xf5, 0x06, 246 + 0xfd, 0x31, 0x16, 0xf6, 0x06, 247 + 0x02, 0x32, 0x0d, 0xf8, 0x07 248 + } 249 + }, 250 + { 251 + .min = 1433, 252 + .max = 1536, 253 + .coef = { 254 + 0xf6, 0x0e, 0x38, 0x0e, 0xf6, 255 + 0xf5, 0x15, 0x38, 0x06, 0xf8, 256 + 0xf5, 0x1d, 0x33, 0x00, 0xfb, 257 + 0xf6, 0x23, 0x2d, 0xfc, 0xfe, 258 + 0xf9, 0x28, 0x26, 0xf9, 0x00, 259 + 0xfc, 0x2c, 0x1e, 0xf7, 0x03, 260 + 0x00, 0x2e, 0x18, 0xf6, 0x04, 261 + 0x05, 0x2e, 0x11, 0xf7, 0x05 262 + } 263 + }, 264 + { 265 + .min = 1536, 266 + .max = 2048, 267 + .coef = { 268 + 0xfb, 0x13, 0x24, 0x13, 0xfb, 269 + 0xfd, 0x17, 0x23, 0x0f, 0xfa, 270 + 0xff, 0x1a, 0x23, 0x0b, 0xf9, 271 + 0x01, 0x1d, 0x22, 0x07, 0xf9, 272 + 0x04, 0x20, 0x1f, 0x04, 0xf9, 273 + 0x07, 0x22, 0x1c, 0x01, 0xfa, 274 + 0x0b, 0x24, 0x17, 0xff, 0xfb, 275 + 0x0f, 0x24, 0x14, 0xfd, 0xfc 276 + } 277 + }, 278 + { 279 + .min = 2048, 280 + .max = 3072, 281 + .coef = { 282 + 0x05, 0x10, 0x16, 0x10, 0x05, 283 + 0x06, 0x11, 0x16, 0x0f, 0x04, 284 + 0x08, 0x13, 0x15, 0x0e, 0x02, 285 + 0x09, 0x14, 0x16, 0x0c, 0x01, 286 + 0x0b, 0x15, 0x15, 0x0b, 0x00, 287 + 0x0d, 0x16, 0x13, 0x0a, 0x00, 288 + 0x0f, 0x17, 0x13, 0x08, 0xff, 289 + 0x11, 0x18, 0x12, 0x07, 0xfe 290 + } 291 + }, 292 + { 293 + .min = 3072, 294 + .max = 4096, 295 + .coef = { 296 + 0x09, 0x0f, 0x10, 0x0f, 0x09, 297 + 0x09, 0x0f, 0x12, 0x0e, 0x08, 298 + 0x0a, 0x10, 0x11, 0x0e, 0x07, 299 + 0x0b, 0x11, 0x11, 0x0d, 0x06, 300 + 0x0c, 0x11, 0x12, 0x0c, 0x05, 301 + 0x0d, 0x12, 0x11, 0x0c, 0x04, 302 + 0x0e, 0x12, 0x11, 0x0b, 0x04, 303 + 0x0f, 0x13, 0x11, 0x0a, 0x03 304 + } 305 + }, 306 + { 307 + .min = 4096, 308 + .max = 5120, 309 + .coef = { 310 + 0x0a, 0x0e, 0x10, 0x0e, 0x0a, 311 + 0x0b, 0x0e, 0x0f, 0x0e, 0x0a, 312 + 0x0b, 0x0f, 0x10, 0x0d, 0x09, 313 + 0x0c, 0x0f, 0x10, 0x0d, 0x08, 314 + 0x0d, 0x0f, 0x0f, 0x0d, 0x08, 315 + 0x0d, 0x10, 0x10, 0x0c, 0x07, 316 + 0x0e, 0x10, 0x0f, 0x0c, 0x07, 317 + 0x0f, 0x10, 0x10, 0x0b, 0x06 318 + } 319 + }, 320 + { 321 + .min = 5120, 322 + .max = 65535, 323 + .coef = { 324 + 0x0b, 0x0e, 0x0e, 0x0e, 0x0b, 325 + 0x0b, 0x0e, 0x0f, 0x0d, 0x0b, 326 + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, 327 + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, 328 + 0x0d, 0x0f, 0x0e, 0x0d, 0x09, 329 + 0x0d, 0x0f, 0x0f, 0x0c, 0x09, 330 + 0x0e, 0x0f, 0x0e, 0x0c, 0x09, 331 + 0x0e, 0x0f, 0x0f, 0x0c, 0x08 332 + } 333 + } 334 + }; 335 + 336 + #define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec) 337 + #define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec) 338 + 339 + /* RGB YUV 601 standard conversion */ 340 + static const u32 bdisp_rgb_to_yuv[] = { 341 + 0x0e1e8bee, 0x08420419, 0xfb5ed471, 0x08004080, 342 + }; 343 + 344 + static const u32 bdisp_yuv_to_rgb[] = { 345 + 0x3324a800, 0xe604ab9c, 0x0004a957, 0x32121eeb, 346 + };
+783
drivers/media/platform/sti/bdisp/bdisp-hw.c
··· 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 + 20 + enum bdisp_target_plan { 21 + BDISP_RGB, 22 + BDISP_Y, 23 + BDISP_CBCR 24 + }; 25 + 26 + struct 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 + 43 + struct 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 + 50 + static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER]; 51 + static 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 + */ 62 + int 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 + */ 97 + int 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 + */ 126 + void 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 + */ 147 + int 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 + */ 187 + void 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 + */ 209 + int 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 + */ 256 + static 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 + */ 277 + static 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 + */ 300 + static 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 + */ 333 + static 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 + */ 363 + static 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 + */ 419 + static 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 + */ 462 + static 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 + */ 697 + static 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_update 745 + * @ctx: device context 746 + * 747 + * Send the request to the HW 748 + * 749 + * RETURNS: 750 + * 0 on success 751 + */ 752 + int bdisp_hw_update(struct bdisp_ctx *ctx) 753 + { 754 + int ret; 755 + struct bdisp_dev *bdisp = ctx->bdisp_dev; 756 + struct device *dev = bdisp->dev; 757 + unsigned int node_id; 758 + 759 + dev_dbg(dev, "%s\n", __func__); 760 + 761 + /* build nodes */ 762 + ret = bdisp_hw_build_all_nodes(ctx); 763 + if (ret) { 764 + dev_err(dev, "cannot build nodes (%d)\n", ret); 765 + return ret; 766 + } 767 + 768 + /* Configure interrupt to 'Last Node Reached for AQ1' */ 769 + writel(BLT_AQ1_CTL_CFG, bdisp->regs + BLT_AQ1_CTL); 770 + writel(BLT_ITS_AQ1_LNA, bdisp->regs + BLT_ITM0); 771 + 772 + /* Write first node addr */ 773 + writel(ctx->node_paddr[0], bdisp->regs + BLT_AQ1_IP); 774 + 775 + /* Find and write last node addr : this starts the HW processing */ 776 + for (node_id = 0; node_id < MAX_NB_NODE - 1; node_id++) { 777 + if (!ctx->node[node_id]->nip) 778 + break; 779 + } 780 + writel(ctx->node_paddr[node_id], bdisp->regs + BLT_AQ1_LNA); 781 + 782 + return 0; 783 + }
+235
drivers/media/platform/sti/bdisp/bdisp-reg.h
··· 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 + struct bdisp_node { 8 + /* 0 - General */ 9 + u32 nip; 10 + u32 cic; 11 + u32 ins; 12 + u32 ack; 13 + /* 1 - Target */ 14 + u32 tba; 15 + u32 tty; 16 + u32 txy; 17 + u32 tsz; 18 + /* 2 - Color Fill */ 19 + u32 s1cf; 20 + u32 s2cf; 21 + /* 3 - Source 1 */ 22 + u32 s1ba; 23 + u32 s1ty; 24 + u32 s1xy; 25 + u32 s1sz_tsz; 26 + /* 4 - Source 2 */ 27 + u32 s2ba; 28 + u32 s2ty; 29 + u32 s2xy; 30 + u32 s2sz; 31 + /* 5 - Source 3 */ 32 + u32 s3ba; 33 + u32 s3ty; 34 + u32 s3xy; 35 + u32 s3sz; 36 + /* 6 - Clipping */ 37 + u32 cwo; 38 + u32 cws; 39 + /* 7 - CLUT */ 40 + u32 cco; 41 + u32 cml; 42 + /* 8 - Filter & Mask */ 43 + u32 fctl; 44 + u32 pmk; 45 + /* 9 - Chroma Filter */ 46 + u32 rsf; 47 + u32 rzi; 48 + u32 hfp; 49 + u32 vfp; 50 + /* 10 - Luma Filter */ 51 + u32 y_rsf; 52 + u32 y_rzi; 53 + u32 y_hfp; 54 + u32 y_vfp; 55 + /* 11 - Flicker */ 56 + u32 ff0; 57 + u32 ff1; 58 + u32 ff2; 59 + u32 ff3; 60 + /* 12 - Color Key */ 61 + u32 key1; 62 + u32 key2; 63 + /* 14 - Static Address & User */ 64 + u32 sar; 65 + u32 usr; 66 + /* 15 - Input Versatile Matrix */ 67 + u32 ivmx0; 68 + u32 ivmx1; 69 + u32 ivmx2; 70 + u32 ivmx3; 71 + /* 16 - Output Versatile Matrix */ 72 + u32 ovmx0; 73 + u32 ovmx1; 74 + u32 ovmx2; 75 + u32 ovmx3; 76 + /* 17 - Pace */ 77 + u32 pace; 78 + /* 18 - VC1R & DEI */ 79 + u32 vc1r; 80 + u32 dei; 81 + /* 19 - Gradient Fill */ 82 + u32 hgf; 83 + u32 vgf; 84 + }; 85 + 86 + /* HW registers : static */ 87 + #define BLT_CTL 0x0A00 88 + #define BLT_ITS 0x0A04 89 + #define BLT_STA1 0x0A08 90 + #define BLT_AQ1_CTL 0x0A60 91 + #define BLT_AQ1_IP 0x0A64 92 + #define BLT_AQ1_LNA 0x0A68 93 + #define BLT_AQ1_STA 0x0A6C 94 + #define BLT_ITM0 0x0AD0 95 + /* HW registers : plugs */ 96 + #define BLT_PLUGS1_OP2 0x0B04 97 + #define BLT_PLUGS1_CHZ 0x0B08 98 + #define BLT_PLUGS1_MSZ 0x0B0C 99 + #define BLT_PLUGS1_PGZ 0x0B10 100 + #define BLT_PLUGS2_OP2 0x0B24 101 + #define BLT_PLUGS2_CHZ 0x0B28 102 + #define BLT_PLUGS2_MSZ 0x0B2C 103 + #define BLT_PLUGS2_PGZ 0x0B30 104 + #define BLT_PLUGS3_OP2 0x0B44 105 + #define BLT_PLUGS3_CHZ 0x0B48 106 + #define BLT_PLUGS3_MSZ 0x0B4C 107 + #define BLT_PLUGS3_PGZ 0x0B50 108 + #define BLT_PLUGT_OP2 0x0B84 109 + #define BLT_PLUGT_CHZ 0x0B88 110 + #define BLT_PLUGT_MSZ 0x0B8C 111 + #define BLT_PLUGT_PGZ 0x0B90 112 + /* HW registers : node */ 113 + #define BLT_NIP 0x0C00 114 + #define BLT_CIC 0x0C04 115 + #define BLT_INS 0x0C08 116 + #define BLT_ACK 0x0C0C 117 + #define BLT_TBA 0x0C10 118 + #define BLT_TTY 0x0C14 119 + #define BLT_TXY 0x0C18 120 + #define BLT_TSZ 0x0C1C 121 + #define BLT_S1BA 0x0C28 122 + #define BLT_S1TY 0x0C2C 123 + #define BLT_S1XY 0x0C30 124 + #define BLT_S2BA 0x0C38 125 + #define BLT_S2TY 0x0C3C 126 + #define BLT_S2XY 0x0C40 127 + #define BLT_S2SZ 0x0C44 128 + #define BLT_S3BA 0x0C48 129 + #define BLT_S3TY 0x0C4C 130 + #define BLT_S3XY 0x0C50 131 + #define BLT_S3SZ 0x0C54 132 + #define BLT_FCTL 0x0C68 133 + #define BLT_RSF 0x0C70 134 + #define BLT_RZI 0x0C74 135 + #define BLT_HFP 0x0C78 136 + #define BLT_VFP 0x0C7C 137 + #define BLT_Y_RSF 0x0C80 138 + #define BLT_Y_RZI 0x0C84 139 + #define BLT_Y_HFP 0x0C88 140 + #define BLT_Y_VFP 0x0C8C 141 + #define BLT_IVMX0 0x0CC0 142 + #define BLT_IVMX1 0x0CC4 143 + #define BLT_IVMX2 0x0CC8 144 + #define BLT_IVMX3 0x0CCC 145 + #define BLT_OVMX0 0x0CD0 146 + #define BLT_OVMX1 0x0CD4 147 + #define BLT_OVMX2 0x0CD8 148 + #define BLT_OVMX3 0x0CDC 149 + #define BLT_DEI 0x0CEC 150 + /* HW registers : filters */ 151 + #define BLT_HFC_N 0x0D00 152 + #define BLT_VFC_N 0x0D90 153 + #define BLT_Y_HFC_N 0x0E00 154 + #define BLT_Y_VFC_N 0x0E90 155 + #define BLT_NB_H_COEF 16 156 + #define BLT_NB_V_COEF 10 157 + 158 + /* Registers values */ 159 + #define BLT_CTL_RESET BIT(31) /* Global soft reset */ 160 + 161 + #define BLT_ITS_AQ1_LNA BIT(12) /* AQ1 LNA reached */ 162 + 163 + #define BLT_STA1_IDLE BIT(0) /* BDISP idle */ 164 + 165 + #define BLT_AQ1_CTL_CFG 0x80400003 /* Enable, P3, LNA reached */ 166 + 167 + #define BLT_INS_S1_MASK (BIT(0) | BIT(1) | BIT(2)) 168 + #define BLT_INS_S1_OFF 0x00000000 /* src1 disabled */ 169 + #define BLT_INS_S1_MEM 0x00000001 /* src1 fetched from memory */ 170 + #define BLT_INS_S1_CF 0x00000003 /* src1 color fill */ 171 + #define BLT_INS_S1_COPY 0x00000004 /* src1 direct copy */ 172 + #define BLT_INS_S1_FILL 0x00000007 /* src1 firect fill */ 173 + #define BLT_INS_S2_MASK (BIT(3) | BIT(4)) 174 + #define BLT_INS_S2_OFF 0x00000000 /* src2 disabled */ 175 + #define BLT_INS_S2_MEM 0x00000008 /* src2 fetched from memory */ 176 + #define BLT_INS_S2_CF 0x00000018 /* src2 color fill */ 177 + #define BLT_INS_S3_MASK BIT(5) 178 + #define BLT_INS_S3_OFF 0x00000000 /* src3 disabled */ 179 + #define BLT_INS_S3_MEM 0x00000020 /* src3 fetched from memory */ 180 + #define BLT_INS_IVMX BIT(6) /* Input versatile matrix */ 181 + #define BLT_INS_CLUT BIT(7) /* Color Look Up Table */ 182 + #define BLT_INS_SCALE BIT(8) /* Scaling */ 183 + #define BLT_INS_FLICK BIT(9) /* Flicker filter */ 184 + #define BLT_INS_CLIP BIT(10) /* Clipping */ 185 + #define BLT_INS_CKEY BIT(11) /* Color key */ 186 + #define BLT_INS_OVMX BIT(12) /* Output versatile matrix */ 187 + #define BLT_INS_DEI BIT(13) /* Deinterlace */ 188 + #define BLT_INS_PMASK BIT(14) /* Plane mask */ 189 + #define BLT_INS_VC1R BIT(17) /* VC1 Range mapping */ 190 + #define BLT_INS_ROTATE BIT(18) /* Rotation */ 191 + #define BLT_INS_GRAD BIT(19) /* Gradient fill */ 192 + #define BLT_INS_AQLOCK BIT(29) /* AQ lock */ 193 + #define BLT_INS_PACE BIT(30) /* Pace down */ 194 + #define BLT_INS_IRQ BIT(31) /* Raise IRQ when node done */ 195 + #define BLT_CIC_ALL_GRP 0x000FDFFC /* all valid groups present */ 196 + #define BLT_ACK_BYPASS_S2S3 0x00000007 /* Bypass src2 and src3 */ 197 + 198 + #define BLT_TTY_COL_SHIFT 16 /* Color format */ 199 + #define BLT_TTY_COL_MASK 0x001F0000 /* Color format mask */ 200 + #define BLT_TTY_ALPHA_R BIT(21) /* Alpha range */ 201 + #define BLT_TTY_CR_NOT_CB BIT(22) /* CR not Cb */ 202 + #define BLT_TTY_MB BIT(23) /* MB frame / field*/ 203 + #define BLT_TTY_HSO BIT(24) /* H scan order */ 204 + #define BLT_TTY_VSO BIT(25) /* V scan order */ 205 + #define BLT_TTY_DITHER BIT(26) /* Dithering */ 206 + #define BLT_TTY_CHROMA BIT(27) /* Write chroma / luma */ 207 + #define BLT_TTY_BIG_END BIT(30) /* Big endianness */ 208 + 209 + #define BLT_S1TY_A1_SUBSET BIT(22) /* A1 subset */ 210 + #define BLT_S1TY_CHROMA_EXT BIT(26) /* Chroma Extended */ 211 + #define BTL_S1TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */ 212 + #define BLT_S1TY_RGB_EXP BIT(29) /* RGB expansion mode */ 213 + 214 + #define BLT_S2TY_A1_SUBSET BIT(22) /* A1 subset */ 215 + #define BLT_S2TY_CHROMA_EXT BIT(26) /* Chroma Extended */ 216 + #define BTL_S2TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */ 217 + #define BLT_S2TY_RGB_EXP BIT(29) /* RGB expansion mode */ 218 + 219 + #define BLT_S3TY_BLANK_ACC BIT(26) /* Blank access */ 220 + 221 + #define BLT_FCTL_HV_SCALE 0x00000055 /* H/V resize + color filter */ 222 + #define BLT_FCTL_Y_HV_SCALE 0x33000000 /* Luma version */ 223 + 224 + #define BLT_FCTL_HV_SAMPLE 0x00000044 /* H/V resize */ 225 + #define BLT_FCTL_Y_HV_SAMPLE 0x22000000 /* Luma version */ 226 + 227 + #define BLT_RZI_DEFAULT 0x20003000 /* H/VNB_repeat = 3/2 */ 228 + 229 + /* Color format */ 230 + #define BDISP_RGB565 0x00 /* RGB565 */ 231 + #define BDISP_RGB888 0x01 /* RGB888 */ 232 + #define BDISP_XRGB8888 0x02 /* RGB888_32 */ 233 + #define BDISP_ARGB8888 0x05 /* ARGB888 */ 234 + #define BDISP_NV12 0x16 /* YCbCr42x R2B */ 235 + #define BDISP_YUV_3B 0x1E /* YUV (3 buffer) */
+1405
drivers/media/platform/sti/bdisp/bdisp-v4l2.c
··· 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/errno.h> 8 + #include <linux/interrupt.h> 9 + #include <linux/kernel.h> 10 + #include <linux/module.h> 11 + #include <linux/of.h> 12 + #include <linux/pm_runtime.h> 13 + #include <linux/slab.h> 14 + 15 + #include <media/v4l2-event.h> 16 + #include <media/v4l2-ioctl.h> 17 + 18 + #include "bdisp.h" 19 + 20 + #define BDISP_MAX_CTRL_NUM 10 21 + 22 + #define BDISP_WORK_TIMEOUT ((100 * HZ) / 1000) 23 + 24 + /* User configuration change */ 25 + #define BDISP_PARAMS BIT(0) /* Config updated */ 26 + #define BDISP_SRC_FMT BIT(1) /* Source set */ 27 + #define BDISP_DST_FMT BIT(2) /* Destination set */ 28 + #define BDISP_CTX_STOP_REQ BIT(3) /* Stop request */ 29 + #define BDISP_CTX_ABORT BIT(4) /* Abort while device run */ 30 + 31 + #define BDISP_MIN_W 1 32 + #define BDISP_MAX_W 8191 33 + #define BDISP_MIN_H 1 34 + #define BDISP_MAX_H 8191 35 + 36 + #define fh_to_ctx(__fh) container_of(__fh, struct bdisp_ctx, fh) 37 + 38 + enum bdisp_dev_flags { 39 + ST_M2M_OPEN, /* Driver opened */ 40 + ST_M2M_RUNNING, /* HW device running */ 41 + ST_M2M_SUSPENDED, /* Driver suspended */ 42 + ST_M2M_SUSPENDING, /* Driver being suspended */ 43 + }; 44 + 45 + static const struct bdisp_fmt bdisp_formats[] = { 46 + /* ARGB888. [31:0] A:R:G:B 8:8:8:8 little endian */ 47 + { 48 + .pixelformat = V4L2_PIX_FMT_ABGR32, /* is actually ARGB */ 49 + .nb_planes = 1, 50 + .bpp = 32, 51 + .bpp_plane0 = 32, 52 + .w_align = 1, 53 + .h_align = 1 54 + }, 55 + /* XRGB888. [31:0] x:R:G:B 8:8:8:8 little endian */ 56 + { 57 + .pixelformat = V4L2_PIX_FMT_XBGR32, /* is actually xRGB */ 58 + .nb_planes = 1, 59 + .bpp = 32, 60 + .bpp_plane0 = 32, 61 + .w_align = 1, 62 + .h_align = 1 63 + }, 64 + /* RGB565. [15:0] R:G:B 5:6:5 little endian */ 65 + { 66 + .pixelformat = V4L2_PIX_FMT_RGB565, 67 + .nb_planes = 1, 68 + .bpp = 16, 69 + .bpp_plane0 = 16, 70 + .w_align = 1, 71 + .h_align = 1 72 + }, 73 + /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */ 74 + { 75 + .pixelformat = V4L2_PIX_FMT_NV12, 76 + .nb_planes = 2, 77 + .bpp = 12, 78 + .bpp_plane0 = 8, 79 + .w_align = 2, 80 + .h_align = 2 81 + }, 82 + /* RGB888. [23:0] B:G:R 8:8:8 little endian */ 83 + { 84 + .pixelformat = V4L2_PIX_FMT_RGB24, 85 + .nb_planes = 1, 86 + .bpp = 24, 87 + .bpp_plane0 = 24, 88 + .w_align = 1, 89 + .h_align = 1 90 + }, 91 + /* YU12. YUV420P - 1 plane for Y + 1 plane for Cb + 1 plane for Cr 92 + * To keep as the LAST element of this table (no support on capture) 93 + */ 94 + { 95 + .pixelformat = V4L2_PIX_FMT_YUV420, 96 + .nb_planes = 3, 97 + .bpp = 12, 98 + .bpp_plane0 = 8, 99 + .w_align = 2, 100 + .h_align = 2 101 + } 102 + }; 103 + 104 + /* Default format : HD ARGB32*/ 105 + #define BDISP_DEF_WIDTH 1920 106 + #define BDISP_DEF_HEIGHT 1080 107 + 108 + static const struct bdisp_frame bdisp_dflt_fmt = { 109 + .width = BDISP_DEF_WIDTH, 110 + .height = BDISP_DEF_HEIGHT, 111 + .fmt = &bdisp_formats[0], 112 + .field = V4L2_FIELD_NONE, 113 + .bytesperline = BDISP_DEF_WIDTH * 4, 114 + .sizeimage = BDISP_DEF_WIDTH * BDISP_DEF_HEIGHT * 4, 115 + .colorspace = V4L2_COLORSPACE_REC709, 116 + .crop = {0, 0, BDISP_DEF_WIDTH, BDISP_DEF_HEIGHT}, 117 + .paddr = {0, 0, 0, 0} 118 + }; 119 + 120 + static inline void bdisp_ctx_state_lock_set(u32 state, struct bdisp_ctx *ctx) 121 + { 122 + unsigned long flags; 123 + 124 + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 125 + ctx->state |= state; 126 + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 127 + } 128 + 129 + static inline void bdisp_ctx_state_lock_clear(u32 state, struct bdisp_ctx *ctx) 130 + { 131 + unsigned long flags; 132 + 133 + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 134 + ctx->state &= ~state; 135 + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 136 + } 137 + 138 + static inline bool bdisp_ctx_state_is_set(u32 mask, struct bdisp_ctx *ctx) 139 + { 140 + unsigned long flags; 141 + bool ret; 142 + 143 + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 144 + ret = (ctx->state & mask) == mask; 145 + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 146 + 147 + return ret; 148 + } 149 + 150 + static const struct bdisp_fmt *bdisp_find_fmt(u32 pixelformat) 151 + { 152 + const struct bdisp_fmt *fmt; 153 + unsigned int i; 154 + 155 + for (i = 0; i < ARRAY_SIZE(bdisp_formats); i++) { 156 + fmt = &bdisp_formats[i]; 157 + if (fmt->pixelformat == pixelformat) 158 + return fmt; 159 + } 160 + 161 + return NULL; 162 + } 163 + 164 + static struct bdisp_frame *ctx_get_frame(struct bdisp_ctx *ctx, 165 + enum v4l2_buf_type type) 166 + { 167 + switch (type) { 168 + case V4L2_BUF_TYPE_VIDEO_OUTPUT: 169 + return &ctx->src; 170 + case V4L2_BUF_TYPE_VIDEO_CAPTURE: 171 + return &ctx->dst; 172 + default: 173 + dev_err(ctx->bdisp_dev->dev, 174 + "Wrong buffer/video queue type (%d)\n", type); 175 + break; 176 + } 177 + 178 + return ERR_PTR(-EINVAL); 179 + } 180 + 181 + static void bdisp_job_finish(struct bdisp_ctx *ctx, int vb_state) 182 + { 183 + struct vb2_buffer *src_vb, *dst_vb; 184 + 185 + if (WARN(!ctx || !ctx->fh.m2m_ctx, "Null hardware context\n")) 186 + return; 187 + 188 + dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); 189 + 190 + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 191 + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 192 + 193 + if (src_vb && dst_vb) { 194 + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; 195 + dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode; 196 + dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 197 + dst_vb->v4l2_buf.flags |= src_vb->v4l2_buf.flags & 198 + V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 199 + 200 + v4l2_m2m_buf_done(src_vb, vb_state); 201 + v4l2_m2m_buf_done(dst_vb, vb_state); 202 + 203 + v4l2_m2m_job_finish(ctx->bdisp_dev->m2m.m2m_dev, 204 + ctx->fh.m2m_ctx); 205 + } 206 + } 207 + 208 + static int bdisp_ctx_stop_req(struct bdisp_ctx *ctx) 209 + { 210 + struct bdisp_ctx *curr_ctx; 211 + struct bdisp_dev *bdisp = ctx->bdisp_dev; 212 + int ret; 213 + 214 + dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); 215 + 216 + cancel_delayed_work(&bdisp->timeout_work); 217 + 218 + curr_ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 219 + if (!test_bit(ST_M2M_RUNNING, &bdisp->state) || (curr_ctx != ctx)) 220 + return 0; 221 + 222 + bdisp_ctx_state_lock_set(BDISP_CTX_STOP_REQ, ctx); 223 + 224 + ret = wait_event_timeout(bdisp->irq_queue, 225 + !bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx), 226 + BDISP_WORK_TIMEOUT); 227 + 228 + if (!ret) { 229 + dev_err(ctx->bdisp_dev->dev, "%s IRQ timeout\n", __func__); 230 + return -ETIMEDOUT; 231 + } 232 + 233 + return 0; 234 + } 235 + 236 + static void __bdisp_job_abort(struct bdisp_ctx *ctx) 237 + { 238 + int ret; 239 + 240 + ret = bdisp_ctx_stop_req(ctx); 241 + if ((ret == -ETIMEDOUT) || (ctx->state & BDISP_CTX_ABORT)) { 242 + bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ | BDISP_CTX_ABORT, 243 + ctx); 244 + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 245 + } 246 + } 247 + 248 + static void bdisp_job_abort(void *priv) 249 + { 250 + __bdisp_job_abort((struct bdisp_ctx *)priv); 251 + } 252 + 253 + static int bdisp_get_addr(struct bdisp_ctx *ctx, struct vb2_buffer *vb, 254 + struct bdisp_frame *frame, dma_addr_t *paddr) 255 + { 256 + if (!vb || !frame) 257 + return -EINVAL; 258 + 259 + paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0); 260 + 261 + if (frame->fmt->nb_planes > 1) 262 + /* UV (NV12) or U (420P) */ 263 + paddr[1] = (dma_addr_t)(paddr[0] + 264 + frame->bytesperline * frame->height); 265 + 266 + if (frame->fmt->nb_planes > 2) 267 + /* V (420P) */ 268 + paddr[2] = (dma_addr_t)(paddr[1] + 269 + (frame->bytesperline * frame->height) / 4); 270 + 271 + if (frame->fmt->nb_planes > 3) 272 + dev_dbg(ctx->bdisp_dev->dev, "ignoring some planes\n"); 273 + 274 + dev_dbg(ctx->bdisp_dev->dev, 275 + "%s plane[0]=%pad plane[1]=%pad plane[2]=%pad\n", 276 + __func__, &paddr[0], &paddr[1], &paddr[2]); 277 + 278 + return 0; 279 + } 280 + 281 + static int bdisp_get_bufs(struct bdisp_ctx *ctx) 282 + { 283 + struct bdisp_frame *src, *dst; 284 + struct vb2_buffer *src_vb, *dst_vb; 285 + int ret; 286 + 287 + src = &ctx->src; 288 + dst = &ctx->dst; 289 + 290 + src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 291 + ret = bdisp_get_addr(ctx, src_vb, src, src->paddr); 292 + if (ret) 293 + return ret; 294 + 295 + dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 296 + ret = bdisp_get_addr(ctx, dst_vb, dst, dst->paddr); 297 + if (ret) 298 + return ret; 299 + 300 + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; 301 + 302 + return 0; 303 + } 304 + 305 + static void bdisp_device_run(void *priv) 306 + { 307 + struct bdisp_ctx *ctx = priv; 308 + struct bdisp_dev *bdisp; 309 + unsigned long flags; 310 + int err = 0; 311 + 312 + if (WARN(!ctx, "Null hardware context\n")) 313 + return; 314 + 315 + bdisp = ctx->bdisp_dev; 316 + dev_dbg(bdisp->dev, "%s\n", __func__); 317 + spin_lock_irqsave(&bdisp->slock, flags); 318 + 319 + if (bdisp->m2m.ctx != ctx) { 320 + dev_dbg(bdisp->dev, "ctx updated: %p -> %p\n", 321 + bdisp->m2m.ctx, ctx); 322 + ctx->state |= BDISP_PARAMS; 323 + bdisp->m2m.ctx = ctx; 324 + } 325 + 326 + if (ctx->state & BDISP_CTX_STOP_REQ) { 327 + ctx->state &= ~BDISP_CTX_STOP_REQ; 328 + ctx->state |= BDISP_CTX_ABORT; 329 + wake_up(&bdisp->irq_queue); 330 + goto out; 331 + } 332 + 333 + err = bdisp_get_bufs(ctx); 334 + if (err) { 335 + dev_err(bdisp->dev, "cannot get address\n"); 336 + goto out; 337 + } 338 + 339 + err = bdisp_hw_reset(bdisp); 340 + if (err) { 341 + dev_err(bdisp->dev, "could not get HW ready\n"); 342 + goto out; 343 + } 344 + 345 + err = bdisp_hw_update(ctx); 346 + if (err) { 347 + dev_err(bdisp->dev, "could not send HW request\n"); 348 + goto out; 349 + } 350 + 351 + queue_delayed_work(bdisp->work_queue, &bdisp->timeout_work, 352 + BDISP_WORK_TIMEOUT); 353 + set_bit(ST_M2M_RUNNING, &bdisp->state); 354 + out: 355 + ctx->state &= ~BDISP_PARAMS; 356 + spin_unlock_irqrestore(&bdisp->slock, flags); 357 + if (err) 358 + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 359 + } 360 + 361 + static struct v4l2_m2m_ops bdisp_m2m_ops = { 362 + .device_run = bdisp_device_run, 363 + .job_abort = bdisp_job_abort, 364 + }; 365 + 366 + static int __bdisp_s_ctrl(struct bdisp_ctx *ctx, struct v4l2_ctrl *ctrl) 367 + { 368 + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) 369 + return 0; 370 + 371 + switch (ctrl->id) { 372 + case V4L2_CID_HFLIP: 373 + ctx->hflip = ctrl->val; 374 + break; 375 + case V4L2_CID_VFLIP: 376 + ctx->vflip = ctrl->val; 377 + break; 378 + default: 379 + dev_err(ctx->bdisp_dev->dev, "unknown control %d\n", ctrl->id); 380 + return -EINVAL; 381 + } 382 + 383 + ctx->state |= BDISP_PARAMS; 384 + 385 + return 0; 386 + } 387 + 388 + static int bdisp_s_ctrl(struct v4l2_ctrl *ctrl) 389 + { 390 + struct bdisp_ctx *ctx = container_of(ctrl->handler, struct bdisp_ctx, 391 + ctrl_handler); 392 + unsigned long flags; 393 + int ret; 394 + 395 + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 396 + ret = __bdisp_s_ctrl(ctx, ctrl); 397 + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 398 + 399 + return ret; 400 + } 401 + 402 + static const struct v4l2_ctrl_ops bdisp_c_ops = { 403 + .s_ctrl = bdisp_s_ctrl, 404 + }; 405 + 406 + static int bdisp_ctrls_create(struct bdisp_ctx *ctx) 407 + { 408 + if (ctx->ctrls_rdy) 409 + return 0; 410 + 411 + v4l2_ctrl_handler_init(&ctx->ctrl_handler, BDISP_MAX_CTRL_NUM); 412 + 413 + ctx->bdisp_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, 414 + &bdisp_c_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 415 + ctx->bdisp_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, 416 + &bdisp_c_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 417 + 418 + if (ctx->ctrl_handler.error) { 419 + int err = ctx->ctrl_handler.error; 420 + 421 + v4l2_ctrl_handler_free(&ctx->ctrl_handler); 422 + return err; 423 + } 424 + 425 + ctx->ctrls_rdy = true; 426 + 427 + return 0; 428 + } 429 + 430 + static void bdisp_ctrls_delete(struct bdisp_ctx *ctx) 431 + { 432 + if (ctx->ctrls_rdy) { 433 + v4l2_ctrl_handler_free(&ctx->ctrl_handler); 434 + ctx->ctrls_rdy = false; 435 + } 436 + } 437 + 438 + static int bdisp_queue_setup(struct vb2_queue *vq, 439 + const struct v4l2_format *fmt, 440 + unsigned int *nb_buf, unsigned int *nb_planes, 441 + unsigned int sizes[], void *allocators[]) 442 + { 443 + struct bdisp_ctx *ctx = vb2_get_drv_priv(vq); 444 + struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type); 445 + 446 + if (IS_ERR(frame)) { 447 + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 448 + return PTR_ERR(frame); 449 + } 450 + 451 + if (!frame->fmt) { 452 + dev_err(ctx->bdisp_dev->dev, "Invalid format\n"); 453 + return -EINVAL; 454 + } 455 + 456 + if (fmt && fmt->fmt.pix.sizeimage < frame->sizeimage) 457 + return -EINVAL; 458 + 459 + *nb_planes = 1; 460 + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : frame->sizeimage; 461 + allocators[0] = ctx->bdisp_dev->alloc_ctx; 462 + 463 + return 0; 464 + } 465 + 466 + static int bdisp_buf_prepare(struct vb2_buffer *vb) 467 + { 468 + struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 469 + struct bdisp_frame *frame = ctx_get_frame(ctx, vb->vb2_queue->type); 470 + 471 + if (IS_ERR(frame)) { 472 + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 473 + return PTR_ERR(frame); 474 + } 475 + 476 + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 477 + vb2_set_plane_payload(vb, 0, frame->sizeimage); 478 + 479 + return 0; 480 + } 481 + 482 + static void bdisp_buf_queue(struct vb2_buffer *vb) 483 + { 484 + struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 485 + 486 + /* return to V4L2 any 0-size buffer so it can be dequeued by user */ 487 + if (!vb2_get_plane_payload(vb, 0)) { 488 + dev_dbg(ctx->bdisp_dev->dev, "0 data buffer, skip it\n"); 489 + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); 490 + return; 491 + } 492 + 493 + if (ctx->fh.m2m_ctx) 494 + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); 495 + } 496 + 497 + static int bdisp_start_streaming(struct vb2_queue *q, unsigned int count) 498 + { 499 + struct bdisp_ctx *ctx = q->drv_priv; 500 + struct vb2_buffer *buf; 501 + int ret = pm_runtime_get_sync(ctx->bdisp_dev->dev); 502 + 503 + if (ret < 0) { 504 + dev_err(ctx->bdisp_dev->dev, "failed to set runtime PM\n"); 505 + 506 + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 507 + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) 508 + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 509 + } else { 510 + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) 511 + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 512 + } 513 + 514 + return ret; 515 + } 516 + 517 + return 0; 518 + } 519 + 520 + static void bdisp_stop_streaming(struct vb2_queue *q) 521 + { 522 + struct bdisp_ctx *ctx = q->drv_priv; 523 + 524 + __bdisp_job_abort(ctx); 525 + 526 + pm_runtime_put(ctx->bdisp_dev->dev); 527 + } 528 + 529 + static struct vb2_ops bdisp_qops = { 530 + .queue_setup = bdisp_queue_setup, 531 + .buf_prepare = bdisp_buf_prepare, 532 + .buf_queue = bdisp_buf_queue, 533 + .wait_prepare = vb2_ops_wait_prepare, 534 + .wait_finish = vb2_ops_wait_finish, 535 + .stop_streaming = bdisp_stop_streaming, 536 + .start_streaming = bdisp_start_streaming, 537 + }; 538 + 539 + static int queue_init(void *priv, 540 + struct vb2_queue *src_vq, struct vb2_queue *dst_vq) 541 + { 542 + struct bdisp_ctx *ctx = priv; 543 + int ret; 544 + 545 + memset(src_vq, 0, sizeof(*src_vq)); 546 + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 547 + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 548 + src_vq->drv_priv = ctx; 549 + src_vq->ops = &bdisp_qops; 550 + src_vq->mem_ops = &vb2_dma_contig_memops; 551 + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 552 + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 553 + src_vq->lock = &ctx->bdisp_dev->lock; 554 + 555 + ret = vb2_queue_init(src_vq); 556 + if (ret) 557 + return ret; 558 + 559 + memset(dst_vq, 0, sizeof(*dst_vq)); 560 + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 561 + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 562 + dst_vq->drv_priv = ctx; 563 + dst_vq->ops = &bdisp_qops; 564 + dst_vq->mem_ops = &vb2_dma_contig_memops; 565 + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 566 + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 567 + dst_vq->lock = &ctx->bdisp_dev->lock; 568 + 569 + return vb2_queue_init(dst_vq); 570 + } 571 + 572 + static int bdisp_open(struct file *file) 573 + { 574 + struct bdisp_dev *bdisp = video_drvdata(file); 575 + struct bdisp_ctx *ctx = NULL; 576 + int ret; 577 + 578 + if (mutex_lock_interruptible(&bdisp->lock)) 579 + return -ERESTARTSYS; 580 + 581 + /* Allocate memory for both context and node */ 582 + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 583 + if (!ctx) { 584 + ret = -ENOMEM; 585 + goto unlock; 586 + } 587 + ctx->bdisp_dev = bdisp; 588 + 589 + if (bdisp_hw_alloc_nodes(ctx)) { 590 + dev_err(bdisp->dev, "no memory for nodes\n"); 591 + ret = -ENOMEM; 592 + goto mem_ctx; 593 + } 594 + 595 + v4l2_fh_init(&ctx->fh, bdisp->m2m.vdev); 596 + 597 + ret = bdisp_ctrls_create(ctx); 598 + if (ret) { 599 + dev_err(bdisp->dev, "Failed to create control\n"); 600 + goto error_fh; 601 + } 602 + 603 + /* Use separate control handler per file handle */ 604 + ctx->fh.ctrl_handler = &ctx->ctrl_handler; 605 + file->private_data = &ctx->fh; 606 + v4l2_fh_add(&ctx->fh); 607 + 608 + /* Default format */ 609 + ctx->src = bdisp_dflt_fmt; 610 + ctx->dst = bdisp_dflt_fmt; 611 + 612 + /* Setup the device context for mem2mem mode. */ 613 + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(bdisp->m2m.m2m_dev, ctx, 614 + queue_init); 615 + if (IS_ERR(ctx->fh.m2m_ctx)) { 616 + dev_err(bdisp->dev, "Failed to initialize m2m context\n"); 617 + ret = PTR_ERR(ctx->fh.m2m_ctx); 618 + goto error_ctrls; 619 + } 620 + 621 + bdisp->m2m.refcnt++; 622 + set_bit(ST_M2M_OPEN, &bdisp->state); 623 + 624 + dev_dbg(bdisp->dev, "driver opened, ctx = 0x%p\n", ctx); 625 + 626 + mutex_unlock(&bdisp->lock); 627 + 628 + return 0; 629 + 630 + error_ctrls: 631 + bdisp_ctrls_delete(ctx); 632 + error_fh: 633 + v4l2_fh_del(&ctx->fh); 634 + v4l2_fh_exit(&ctx->fh); 635 + bdisp_hw_free_nodes(ctx); 636 + mem_ctx: 637 + kfree(ctx); 638 + unlock: 639 + mutex_unlock(&bdisp->lock); 640 + 641 + return ret; 642 + } 643 + 644 + static int bdisp_release(struct file *file) 645 + { 646 + struct bdisp_ctx *ctx = fh_to_ctx(file->private_data); 647 + struct bdisp_dev *bdisp = ctx->bdisp_dev; 648 + 649 + dev_dbg(bdisp->dev, "%s\n", __func__); 650 + 651 + if (mutex_lock_interruptible(&bdisp->lock)) 652 + return -ERESTARTSYS; 653 + 654 + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 655 + 656 + bdisp_ctrls_delete(ctx); 657 + 658 + v4l2_fh_del(&ctx->fh); 659 + v4l2_fh_exit(&ctx->fh); 660 + 661 + if (--bdisp->m2m.refcnt <= 0) 662 + clear_bit(ST_M2M_OPEN, &bdisp->state); 663 + 664 + bdisp_hw_free_nodes(ctx); 665 + 666 + kfree(ctx); 667 + 668 + mutex_unlock(&bdisp->lock); 669 + 670 + return 0; 671 + } 672 + 673 + static const struct v4l2_file_operations bdisp_fops = { 674 + .owner = THIS_MODULE, 675 + .open = bdisp_open, 676 + .release = bdisp_release, 677 + .poll = v4l2_m2m_fop_poll, 678 + .unlocked_ioctl = video_ioctl2, 679 + .mmap = v4l2_m2m_fop_mmap, 680 + }; 681 + 682 + static int bdisp_querycap(struct file *file, void *fh, 683 + struct v4l2_capability *cap) 684 + { 685 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 686 + struct bdisp_dev *bdisp = ctx->bdisp_dev; 687 + 688 + strlcpy(cap->driver, bdisp->pdev->name, sizeof(cap->driver)); 689 + strlcpy(cap->card, bdisp->pdev->name, sizeof(cap->card)); 690 + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", 691 + BDISP_NAME, bdisp->id); 692 + 693 + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; 694 + 695 + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 696 + 697 + return 0; 698 + } 699 + 700 + static int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 701 + { 702 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 703 + const struct bdisp_fmt *fmt; 704 + 705 + if (f->index >= ARRAY_SIZE(bdisp_formats)) 706 + return -EINVAL; 707 + 708 + fmt = &bdisp_formats[f->index]; 709 + 710 + if ((fmt->pixelformat == V4L2_PIX_FMT_YUV420) && 711 + (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { 712 + dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); 713 + return -EINVAL; 714 + } 715 + f->pixelformat = fmt->pixelformat; 716 + 717 + return 0; 718 + } 719 + 720 + static int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 721 + { 722 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 723 + struct v4l2_pix_format *pix = &f->fmt.pix; 724 + struct bdisp_frame *frame = ctx_get_frame(ctx, f->type); 725 + 726 + if (IS_ERR(frame)) { 727 + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 728 + return PTR_ERR(frame); 729 + } 730 + 731 + pix = &f->fmt.pix; 732 + pix->width = frame->width; 733 + pix->height = frame->height; 734 + pix->pixelformat = frame->fmt->pixelformat; 735 + pix->field = frame->field; 736 + pix->bytesperline = frame->bytesperline; 737 + pix->sizeimage = frame->sizeimage; 738 + pix->colorspace = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 739 + frame->colorspace : bdisp_dflt_fmt.colorspace; 740 + 741 + return 0; 742 + } 743 + 744 + static int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 745 + { 746 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 747 + struct v4l2_pix_format *pix = &f->fmt.pix; 748 + const struct bdisp_fmt *format; 749 + u32 in_w, in_h; 750 + 751 + format = bdisp_find_fmt(pix->pixelformat); 752 + if (!format) { 753 + dev_dbg(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", 754 + pix->pixelformat); 755 + return -EINVAL; 756 + } 757 + 758 + /* YUV420P only supported for VIDEO_OUTPUT */ 759 + if ((format->pixelformat == V4L2_PIX_FMT_YUV420) && 760 + (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { 761 + dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); 762 + return -EINVAL; 763 + } 764 + 765 + /* Field (interlaced only supported on OUTPUT) */ 766 + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || 767 + (pix->field != V4L2_FIELD_INTERLACED)) 768 + pix->field = V4L2_FIELD_NONE; 769 + 770 + /* Adjust width & height */ 771 + in_w = pix->width; 772 + in_h = pix->height; 773 + v4l_bound_align_image(&pix->width, 774 + BDISP_MIN_W, BDISP_MAX_W, 775 + ffs(format->w_align) - 1, 776 + &pix->height, 777 + BDISP_MIN_H, BDISP_MAX_H, 778 + ffs(format->h_align) - 1, 779 + 0); 780 + if ((pix->width != in_w) || (pix->height != in_h)) 781 + dev_dbg(ctx->bdisp_dev->dev, 782 + "%s size updated: %dx%d -> %dx%d\n", __func__, 783 + in_w, in_h, pix->width, pix->height); 784 + 785 + pix->bytesperline = (pix->width * format->bpp_plane0) / 8; 786 + pix->sizeimage = (pix->width * pix->height * format->bpp) / 8; 787 + 788 + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 789 + pix->colorspace = bdisp_dflt_fmt.colorspace; 790 + 791 + return 0; 792 + } 793 + 794 + static int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 795 + { 796 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 797 + struct vb2_queue *vq; 798 + struct bdisp_frame *frame; 799 + struct v4l2_pix_format *pix; 800 + int ret; 801 + u32 state; 802 + 803 + ret = bdisp_try_fmt(file, fh, f); 804 + if (ret) { 805 + dev_err(ctx->bdisp_dev->dev, "Cannot set format\n"); 806 + return ret; 807 + } 808 + 809 + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 810 + if (vb2_is_streaming(vq)) { 811 + dev_err(ctx->bdisp_dev->dev, "queue (%d) busy\n", f->type); 812 + return -EBUSY; 813 + } 814 + 815 + frame = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 816 + &ctx->src : &ctx->dst; 817 + pix = &f->fmt.pix; 818 + frame->fmt = bdisp_find_fmt(pix->pixelformat); 819 + if (!frame->fmt) { 820 + dev_err(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", 821 + pix->pixelformat); 822 + return -EINVAL; 823 + } 824 + 825 + frame->width = pix->width; 826 + frame->height = pix->height; 827 + frame->bytesperline = pix->bytesperline; 828 + frame->sizeimage = pix->sizeimage; 829 + frame->field = pix->field; 830 + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 831 + frame->colorspace = pix->colorspace; 832 + 833 + frame->crop.width = frame->width; 834 + frame->crop.height = frame->height; 835 + frame->crop.left = 0; 836 + frame->crop.top = 0; 837 + 838 + state = BDISP_PARAMS; 839 + state |= (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? 840 + BDISP_DST_FMT : BDISP_SRC_FMT; 841 + bdisp_ctx_state_lock_set(state, ctx); 842 + 843 + return 0; 844 + } 845 + 846 + static int bdisp_g_selection(struct file *file, void *fh, 847 + struct v4l2_selection *s) 848 + { 849 + struct bdisp_frame *frame; 850 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 851 + 852 + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { 853 + /* Composing / capture is not supported */ 854 + dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n"); 855 + return -EINVAL; 856 + } 857 + 858 + frame = ctx_get_frame(ctx, s->type); 859 + if (IS_ERR(frame)) { 860 + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 861 + return PTR_ERR(frame); 862 + } 863 + 864 + switch (s->target) { 865 + case V4L2_SEL_TGT_CROP: 866 + /* cropped frame */ 867 + s->r = frame->crop; 868 + break; 869 + case V4L2_SEL_TGT_CROP_DEFAULT: 870 + case V4L2_SEL_TGT_CROP_BOUNDS: 871 + /* complete frame */ 872 + s->r.left = 0; 873 + s->r.top = 0; 874 + s->r.width = frame->width; 875 + s->r.height = frame->height; 876 + break; 877 + default: 878 + dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n"); 879 + return -EINVAL; 880 + } 881 + 882 + return 0; 883 + } 884 + 885 + static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) 886 + { 887 + /* Return 1 if a is enclosed in b, or 0 otherwise. */ 888 + 889 + if (a->left < b->left || a->top < b->top) 890 + return 0; 891 + 892 + if (a->left + a->width > b->left + b->width) 893 + return 0; 894 + 895 + if (a->top + a->height > b->top + b->height) 896 + return 0; 897 + 898 + return 1; 899 + } 900 + 901 + static int bdisp_s_selection(struct file *file, void *fh, 902 + struct v4l2_selection *s) 903 + { 904 + struct bdisp_frame *frame; 905 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 906 + struct v4l2_rect *in, out; 907 + 908 + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { 909 + /* Composing / capture is not supported */ 910 + dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n"); 911 + return -EINVAL; 912 + } 913 + 914 + if (s->target != V4L2_SEL_TGT_CROP) { 915 + dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n"); 916 + return -EINVAL; 917 + } 918 + 919 + frame = ctx_get_frame(ctx, s->type); 920 + if (IS_ERR(frame)) { 921 + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 922 + return PTR_ERR(frame); 923 + } 924 + 925 + in = &s->r; 926 + out = *in; 927 + 928 + /* Align and check origin */ 929 + out.left = ALIGN(in->left, frame->fmt->w_align); 930 + out.top = ALIGN(in->top, frame->fmt->h_align); 931 + 932 + if ((out.left < 0) || (out.left >= frame->width) || 933 + (out.top < 0) || (out.top >= frame->height)) { 934 + dev_err(ctx->bdisp_dev->dev, 935 + "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", 936 + out.width, out.height, out.left, out.top, 937 + frame->width, frame->height); 938 + return -EINVAL; 939 + } 940 + 941 + /* Align and check size */ 942 + out.width = ALIGN(in->width, frame->fmt->w_align); 943 + out.height = ALIGN(in->height, frame->fmt->w_align); 944 + 945 + if ((out.width < 0) || (out.height < 0) || 946 + ((out.left + out.width) > frame->width) || 947 + ((out.top + out.height) > frame->height)) { 948 + dev_err(ctx->bdisp_dev->dev, 949 + "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", 950 + out.width, out.height, out.left, out.top, 951 + frame->width, frame->height); 952 + return -EINVAL; 953 + } 954 + 955 + /* Checks adjust constraints flags */ 956 + if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in)) 957 + return -ERANGE; 958 + 959 + if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out)) 960 + return -ERANGE; 961 + 962 + if ((out.left != in->left) || (out.top != in->top) || 963 + (out.width != in->width) || (out.height != in->height)) { 964 + dev_dbg(ctx->bdisp_dev->dev, 965 + "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n", 966 + __func__, in->width, in->height, in->left, in->top, 967 + out.width, out.height, out.left, out.top); 968 + *in = out; 969 + } 970 + 971 + frame->crop = out; 972 + 973 + bdisp_ctx_state_lock_set(BDISP_PARAMS, ctx); 974 + 975 + return 0; 976 + } 977 + 978 + static int bdisp_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 979 + { 980 + struct bdisp_ctx *ctx = fh_to_ctx(fh); 981 + 982 + if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && 983 + !bdisp_ctx_state_is_set(BDISP_SRC_FMT, ctx)) { 984 + dev_err(ctx->bdisp_dev->dev, "src not defined\n"); 985 + return -EINVAL; 986 + } 987 + 988 + if ((type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && 989 + !bdisp_ctx_state_is_set(BDISP_DST_FMT, ctx)) { 990 + dev_err(ctx->bdisp_dev->dev, "dst not defined\n"); 991 + return -EINVAL; 992 + } 993 + 994 + return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); 995 + } 996 + 997 + static const struct v4l2_ioctl_ops bdisp_ioctl_ops = { 998 + .vidioc_querycap = bdisp_querycap, 999 + .vidioc_enum_fmt_vid_cap = bdisp_enum_fmt, 1000 + .vidioc_enum_fmt_vid_out = bdisp_enum_fmt, 1001 + .vidioc_g_fmt_vid_cap = bdisp_g_fmt, 1002 + .vidioc_g_fmt_vid_out = bdisp_g_fmt, 1003 + .vidioc_try_fmt_vid_cap = bdisp_try_fmt, 1004 + .vidioc_try_fmt_vid_out = bdisp_try_fmt, 1005 + .vidioc_s_fmt_vid_cap = bdisp_s_fmt, 1006 + .vidioc_s_fmt_vid_out = bdisp_s_fmt, 1007 + .vidioc_g_selection = bdisp_g_selection, 1008 + .vidioc_s_selection = bdisp_s_selection, 1009 + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 1010 + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 1011 + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 1012 + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 1013 + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 1014 + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 1015 + .vidioc_streamon = bdisp_streamon, 1016 + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 1017 + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 1018 + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 1019 + }; 1020 + 1021 + static int bdisp_register_device(struct bdisp_dev *bdisp) 1022 + { 1023 + struct platform_device *pdev; 1024 + int ret; 1025 + 1026 + if (!bdisp) 1027 + return -ENODEV; 1028 + 1029 + pdev = bdisp->pdev; 1030 + 1031 + bdisp->vdev.fops = &bdisp_fops; 1032 + bdisp->vdev.ioctl_ops = &bdisp_ioctl_ops; 1033 + bdisp->vdev.release = video_device_release_empty; 1034 + bdisp->vdev.lock = &bdisp->lock; 1035 + bdisp->vdev.vfl_dir = VFL_DIR_M2M; 1036 + bdisp->vdev.v4l2_dev = &bdisp->v4l2_dev; 1037 + snprintf(bdisp->vdev.name, sizeof(bdisp->vdev.name), "%s.%d", 1038 + BDISP_NAME, bdisp->id); 1039 + 1040 + video_set_drvdata(&bdisp->vdev, bdisp); 1041 + 1042 + bdisp->m2m.vdev = &bdisp->vdev; 1043 + bdisp->m2m.m2m_dev = v4l2_m2m_init(&bdisp_m2m_ops); 1044 + if (IS_ERR(bdisp->m2m.m2m_dev)) { 1045 + dev_err(bdisp->dev, "failed to initialize v4l2-m2m device\n"); 1046 + return PTR_ERR(bdisp->m2m.m2m_dev); 1047 + } 1048 + 1049 + ret = video_register_device(&bdisp->vdev, VFL_TYPE_GRABBER, -1); 1050 + if (ret) { 1051 + dev_err(bdisp->dev, 1052 + "%s(): failed to register video device\n", __func__); 1053 + v4l2_m2m_release(bdisp->m2m.m2m_dev); 1054 + return ret; 1055 + } 1056 + 1057 + return 0; 1058 + } 1059 + 1060 + static void bdisp_unregister_device(struct bdisp_dev *bdisp) 1061 + { 1062 + if (!bdisp) 1063 + return; 1064 + 1065 + if (bdisp->m2m.m2m_dev) 1066 + v4l2_m2m_release(bdisp->m2m.m2m_dev); 1067 + 1068 + video_unregister_device(bdisp->m2m.vdev); 1069 + } 1070 + 1071 + static irqreturn_t bdisp_irq_thread(int irq, void *priv) 1072 + { 1073 + struct bdisp_dev *bdisp = priv; 1074 + struct bdisp_ctx *ctx; 1075 + 1076 + spin_lock(&bdisp->slock); 1077 + 1078 + cancel_delayed_work(&bdisp->timeout_work); 1079 + 1080 + if (!test_and_clear_bit(ST_M2M_RUNNING, &bdisp->state)) 1081 + goto isr_unlock; 1082 + 1083 + if (test_and_clear_bit(ST_M2M_SUSPENDING, &bdisp->state)) { 1084 + set_bit(ST_M2M_SUSPENDED, &bdisp->state); 1085 + wake_up(&bdisp->irq_queue); 1086 + goto isr_unlock; 1087 + } 1088 + 1089 + ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 1090 + if (!ctx || !ctx->fh.m2m_ctx) 1091 + goto isr_unlock; 1092 + 1093 + spin_unlock(&bdisp->slock); 1094 + 1095 + bdisp_job_finish(ctx, VB2_BUF_STATE_DONE); 1096 + 1097 + if (bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx)) { 1098 + bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ, ctx); 1099 + wake_up(&bdisp->irq_queue); 1100 + } 1101 + 1102 + return IRQ_HANDLED; 1103 + 1104 + isr_unlock: 1105 + spin_unlock(&bdisp->slock); 1106 + 1107 + return IRQ_HANDLED; 1108 + } 1109 + 1110 + static irqreturn_t bdisp_irq_handler(int irq, void *priv) 1111 + { 1112 + if (bdisp_hw_get_and_clear_irq((struct bdisp_dev *)priv)) 1113 + return IRQ_NONE; 1114 + else 1115 + return IRQ_WAKE_THREAD; 1116 + } 1117 + 1118 + static void bdisp_irq_timeout(struct work_struct *ptr) 1119 + { 1120 + struct delayed_work *twork = to_delayed_work(ptr); 1121 + struct bdisp_dev *bdisp = container_of(twork, struct bdisp_dev, 1122 + timeout_work); 1123 + struct bdisp_ctx *ctx; 1124 + 1125 + ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 1126 + 1127 + dev_err(ctx->bdisp_dev->dev, "Device work timeout\n"); 1128 + 1129 + spin_lock(&bdisp->slock); 1130 + clear_bit(ST_M2M_RUNNING, &bdisp->state); 1131 + spin_unlock(&bdisp->slock); 1132 + 1133 + bdisp_hw_reset(bdisp); 1134 + 1135 + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 1136 + } 1137 + 1138 + static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) 1139 + { 1140 + unsigned long flags; 1141 + int timeout; 1142 + 1143 + spin_lock_irqsave(&bdisp->slock, flags); 1144 + if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { 1145 + spin_unlock_irqrestore(&bdisp->slock, flags); 1146 + return 0; 1147 + } 1148 + clear_bit(ST_M2M_SUSPENDED, &bdisp->state); 1149 + set_bit(ST_M2M_SUSPENDING, &bdisp->state); 1150 + spin_unlock_irqrestore(&bdisp->slock, flags); 1151 + 1152 + timeout = wait_event_timeout(bdisp->irq_queue, 1153 + test_bit(ST_M2M_SUSPENDED, &bdisp->state), 1154 + BDISP_WORK_TIMEOUT); 1155 + 1156 + clear_bit(ST_M2M_SUSPENDING, &bdisp->state); 1157 + 1158 + if (!timeout) { 1159 + dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); 1160 + return -EAGAIN; 1161 + } 1162 + 1163 + return 0; 1164 + } 1165 + 1166 + static int bdisp_m2m_resume(struct bdisp_dev *bdisp) 1167 + { 1168 + struct bdisp_ctx *ctx; 1169 + unsigned long flags; 1170 + 1171 + spin_lock_irqsave(&bdisp->slock, flags); 1172 + ctx = bdisp->m2m.ctx; 1173 + bdisp->m2m.ctx = NULL; 1174 + spin_unlock_irqrestore(&bdisp->slock, flags); 1175 + 1176 + if (test_and_clear_bit(ST_M2M_SUSPENDED, &bdisp->state)) 1177 + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 1178 + 1179 + return 0; 1180 + } 1181 + 1182 + static int bdisp_runtime_resume(struct device *dev) 1183 + { 1184 + struct bdisp_dev *bdisp = dev_get_drvdata(dev); 1185 + int ret = clk_enable(bdisp->clock); 1186 + 1187 + if (ret) 1188 + return ret; 1189 + 1190 + return bdisp_m2m_resume(bdisp); 1191 + } 1192 + 1193 + static int bdisp_runtime_suspend(struct device *dev) 1194 + { 1195 + struct bdisp_dev *bdisp = dev_get_drvdata(dev); 1196 + int ret = bdisp_m2m_suspend(bdisp); 1197 + 1198 + if (!ret) 1199 + clk_disable(bdisp->clock); 1200 + 1201 + return ret; 1202 + } 1203 + 1204 + static int bdisp_resume(struct device *dev) 1205 + { 1206 + struct bdisp_dev *bdisp = dev_get_drvdata(dev); 1207 + unsigned long flags; 1208 + int opened; 1209 + 1210 + spin_lock_irqsave(&bdisp->slock, flags); 1211 + opened = test_bit(ST_M2M_OPEN, &bdisp->state); 1212 + spin_unlock_irqrestore(&bdisp->slock, flags); 1213 + 1214 + if (!opened) 1215 + return 0; 1216 + 1217 + if (!pm_runtime_suspended(dev)) 1218 + return bdisp_runtime_resume(dev); 1219 + 1220 + return 0; 1221 + } 1222 + 1223 + static int bdisp_suspend(struct device *dev) 1224 + { 1225 + if (!pm_runtime_suspended(dev)) 1226 + return bdisp_runtime_suspend(dev); 1227 + 1228 + return 0; 1229 + } 1230 + 1231 + static const struct dev_pm_ops bdisp_pm_ops = { 1232 + .suspend = bdisp_suspend, 1233 + .resume = bdisp_resume, 1234 + .runtime_suspend = bdisp_runtime_suspend, 1235 + .runtime_resume = bdisp_runtime_resume, 1236 + }; 1237 + 1238 + static int bdisp_remove(struct platform_device *pdev) 1239 + { 1240 + struct bdisp_dev *bdisp = platform_get_drvdata(pdev); 1241 + 1242 + bdisp_unregister_device(bdisp); 1243 + 1244 + bdisp_hw_free_filters(bdisp->dev); 1245 + 1246 + vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); 1247 + 1248 + pm_runtime_disable(&pdev->dev); 1249 + 1250 + v4l2_device_unregister(&bdisp->v4l2_dev); 1251 + 1252 + if (!IS_ERR(bdisp->clock)) 1253 + clk_unprepare(bdisp->clock); 1254 + 1255 + dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); 1256 + 1257 + return 0; 1258 + } 1259 + 1260 + static int bdisp_probe(struct platform_device *pdev) 1261 + { 1262 + struct bdisp_dev *bdisp; 1263 + struct resource *res; 1264 + struct device *dev = &pdev->dev; 1265 + int ret; 1266 + 1267 + dev_dbg(dev, "%s\n", __func__); 1268 + 1269 + bdisp = devm_kzalloc(dev, sizeof(struct bdisp_dev), GFP_KERNEL); 1270 + if (!bdisp) 1271 + return -ENOMEM; 1272 + 1273 + bdisp->pdev = pdev; 1274 + bdisp->dev = dev; 1275 + platform_set_drvdata(pdev, bdisp); 1276 + 1277 + if (dev->of_node) 1278 + bdisp->id = of_alias_get_id(pdev->dev.of_node, BDISP_NAME); 1279 + else 1280 + bdisp->id = pdev->id; 1281 + 1282 + init_waitqueue_head(&bdisp->irq_queue); 1283 + INIT_DELAYED_WORK(&bdisp->timeout_work, bdisp_irq_timeout); 1284 + bdisp->work_queue = create_workqueue(BDISP_NAME); 1285 + 1286 + spin_lock_init(&bdisp->slock); 1287 + mutex_init(&bdisp->lock); 1288 + 1289 + /* get resources */ 1290 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1291 + bdisp->regs = devm_ioremap_resource(dev, res); 1292 + if (IS_ERR(bdisp->regs)) { 1293 + dev_err(dev, "failed to get regs\n"); 1294 + return PTR_ERR(bdisp->regs); 1295 + } 1296 + 1297 + bdisp->clock = devm_clk_get(dev, BDISP_NAME); 1298 + if (IS_ERR(bdisp->clock)) { 1299 + dev_err(dev, "failed to get clock\n"); 1300 + return PTR_ERR(bdisp->clock); 1301 + } 1302 + 1303 + ret = clk_prepare(bdisp->clock); 1304 + if (ret < 0) { 1305 + dev_err(dev, "clock prepare failed\n"); 1306 + bdisp->clock = ERR_PTR(-EINVAL); 1307 + return ret; 1308 + } 1309 + 1310 + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 1311 + if (!res) { 1312 + dev_err(dev, "failed to get IRQ resource\n"); 1313 + goto err_clk; 1314 + } 1315 + 1316 + ret = devm_request_threaded_irq(dev, res->start, bdisp_irq_handler, 1317 + bdisp_irq_thread, IRQF_ONESHOT, 1318 + pdev->name, bdisp); 1319 + if (ret) { 1320 + dev_err(dev, "failed to install irq\n"); 1321 + goto err_clk; 1322 + } 1323 + 1324 + /* v4l2 register */ 1325 + ret = v4l2_device_register(dev, &bdisp->v4l2_dev); 1326 + if (ret) { 1327 + dev_err(dev, "failed to register\n"); 1328 + goto err_clk; 1329 + } 1330 + 1331 + /* Power management */ 1332 + pm_runtime_enable(dev); 1333 + ret = pm_runtime_get_sync(dev); 1334 + if (ret < 0) { 1335 + dev_err(dev, "failed to set PM\n"); 1336 + goto err_v4l2; 1337 + } 1338 + 1339 + /* Continuous memory allocator */ 1340 + bdisp->alloc_ctx = vb2_dma_contig_init_ctx(dev); 1341 + if (IS_ERR(bdisp->alloc_ctx)) { 1342 + ret = PTR_ERR(bdisp->alloc_ctx); 1343 + goto err_pm; 1344 + } 1345 + 1346 + /* Filters */ 1347 + if (bdisp_hw_alloc_filters(bdisp->dev)) { 1348 + dev_err(bdisp->dev, "no memory for filters\n"); 1349 + ret = -ENOMEM; 1350 + goto err_vb2_dma; 1351 + } 1352 + 1353 + /* Register */ 1354 + ret = bdisp_register_device(bdisp); 1355 + if (ret) { 1356 + dev_err(dev, "failed to register\n"); 1357 + goto err_filter; 1358 + } 1359 + 1360 + dev_info(dev, "%s%d registered as /dev/video%d\n", BDISP_NAME, 1361 + bdisp->id, bdisp->vdev.num); 1362 + 1363 + pm_runtime_put(dev); 1364 + 1365 + return 0; 1366 + 1367 + err_filter: 1368 + bdisp_hw_free_filters(bdisp->dev); 1369 + err_vb2_dma: 1370 + vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); 1371 + err_pm: 1372 + pm_runtime_put(dev); 1373 + err_v4l2: 1374 + v4l2_device_unregister(&bdisp->v4l2_dev); 1375 + err_clk: 1376 + if (!IS_ERR(bdisp->clock)) 1377 + clk_unprepare(bdisp->clock); 1378 + 1379 + return ret; 1380 + } 1381 + 1382 + static const struct of_device_id bdisp_match_types[] = { 1383 + { 1384 + .compatible = "st,stih407-bdisp", 1385 + }, 1386 + { /* end node */ } 1387 + }; 1388 + 1389 + MODULE_DEVICE_TABLE(of, bdisp_match_types); 1390 + 1391 + static struct platform_driver bdisp_driver = { 1392 + .probe = bdisp_probe, 1393 + .remove = bdisp_remove, 1394 + .driver = { 1395 + .name = BDISP_NAME, 1396 + .of_match_table = bdisp_match_types, 1397 + .pm = &bdisp_pm_ops, 1398 + }, 1399 + }; 1400 + 1401 + module_platform_driver(bdisp_driver); 1402 + 1403 + MODULE_DESCRIPTION("2D blitter for STMicroelectronics SoC"); 1404 + MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); 1405 + MODULE_LICENSE("GPL");
+186
drivers/media/platform/sti/bdisp/bdisp.h
··· 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/clk.h> 8 + #include <linux/ktime.h> 9 + #include <linux/platform_device.h> 10 + #include <linux/spinlock.h> 11 + 12 + #include <media/v4l2-ctrls.h> 13 + #include <media/v4l2-device.h> 14 + #include <media/v4l2-mem2mem.h> 15 + 16 + #include <media/videobuf2-dma-contig.h> 17 + 18 + #define BDISP_NAME "bdisp" 19 + 20 + /* 21 + * Max nb of nodes in node-list: 22 + * - 2 nodes to handle wide 4K pictures 23 + * - 2 nodes to handle two planes (Y & CbCr) */ 24 + #define MAX_OUTPUT_PLANES 2 25 + #define MAX_VERTICAL_STRIDES 2 26 + #define MAX_NB_NODE (MAX_OUTPUT_PLANES * MAX_VERTICAL_STRIDES) 27 + 28 + /* struct bdisp_ctrls - bdisp control set 29 + * @hflip: horizontal flip 30 + * @vflip: vertical flip 31 + */ 32 + struct bdisp_ctrls { 33 + struct v4l2_ctrl *hflip; 34 + struct v4l2_ctrl *vflip; 35 + }; 36 + 37 + /** 38 + * struct bdisp_fmt - driver's internal color format data 39 + * @pixelformat:fourcc code for this format 40 + * @nb_planes: number of planes (ex: [0]=RGB/Y - [1]=Cb/Cr, ...) 41 + * @bpp: bits per pixel (general) 42 + * @bpp_plane0: byte per pixel for the 1st plane 43 + * @w_align: width alignment in pixel (multiple of) 44 + * @h_align: height alignment in pixel (multiple of) 45 + */ 46 + struct bdisp_fmt { 47 + u32 pixelformat; 48 + u8 nb_planes; 49 + u8 bpp; 50 + u8 bpp_plane0; 51 + u8 w_align; 52 + u8 h_align; 53 + }; 54 + 55 + /** 56 + * struct bdisp_frame - frame properties 57 + * 58 + * @width: frame width (including padding) 59 + * @height: frame height (including padding) 60 + * @fmt: pointer to frame format descriptor 61 + * @field: frame / field type 62 + * @bytesperline: stride of the 1st plane 63 + * @sizeimage: image size in bytes 64 + * @colorspace: colorspace 65 + * @crop: crop area 66 + * @paddr: image physical addresses per plane ([0]=RGB/Y - [1]=Cb/Cr, ...) 67 + */ 68 + struct bdisp_frame { 69 + u32 width; 70 + u32 height; 71 + const struct bdisp_fmt *fmt; 72 + enum v4l2_field field; 73 + u32 bytesperline; 74 + u32 sizeimage; 75 + enum v4l2_colorspace colorspace; 76 + struct v4l2_rect crop; 77 + dma_addr_t paddr[4]; 78 + }; 79 + 80 + /** 81 + * struct bdisp_request - bdisp request 82 + * 83 + * @src: source frame properties 84 + * @dst: destination frame properties 85 + * @hflip: horizontal flip 86 + * @vflip: vertical flip 87 + * @nb_req: number of run request 88 + */ 89 + struct bdisp_request { 90 + struct bdisp_frame src; 91 + struct bdisp_frame dst; 92 + unsigned int hflip:1; 93 + unsigned int vflip:1; 94 + int nb_req; 95 + }; 96 + 97 + /** 98 + * struct bdisp_ctx - device context data 99 + * 100 + * @src: source frame properties 101 + * @dst: destination frame properties 102 + * @state: flags to keep track of user configuration 103 + * @hflip: horizontal flip 104 + * @vflip: vertical flip 105 + * @bdisp_dev: the device this context applies to 106 + * @node: node array 107 + * @node_paddr: node physical address array 108 + * @fh: v4l2 file handle 109 + * @ctrl_handler: v4l2 controls handler 110 + * @bdisp_ctrls: bdisp control set 111 + * @ctrls_rdy: true if the control handler is initialized 112 + */ 113 + struct bdisp_ctx { 114 + struct bdisp_frame src; 115 + struct bdisp_frame dst; 116 + u32 state; 117 + unsigned int hflip:1; 118 + unsigned int vflip:1; 119 + struct bdisp_dev *bdisp_dev; 120 + struct bdisp_node *node[MAX_NB_NODE]; 121 + dma_addr_t node_paddr[MAX_NB_NODE]; 122 + struct v4l2_fh fh; 123 + struct v4l2_ctrl_handler ctrl_handler; 124 + struct bdisp_ctrls bdisp_ctrls; 125 + bool ctrls_rdy; 126 + }; 127 + 128 + /** 129 + * struct bdisp_m2m_device - v4l2 memory-to-memory device data 130 + * 131 + * @vdev: video device node for v4l2 m2m mode 132 + * @m2m_dev: v4l2 m2m device data 133 + * @ctx: hardware context data 134 + * @refcnt: reference counter 135 + */ 136 + struct bdisp_m2m_device { 137 + struct video_device *vdev; 138 + struct v4l2_m2m_dev *m2m_dev; 139 + struct bdisp_ctx *ctx; 140 + int refcnt; 141 + }; 142 + 143 + /** 144 + * struct bdisp_dev - abstraction for bdisp entity 145 + * 146 + * @v4l2_dev: v4l2 device 147 + * @vdev: video device 148 + * @pdev: platform device 149 + * @dev: device 150 + * @lock: mutex protecting this data structure 151 + * @slock: spinlock protecting this data structure 152 + * @id: device index 153 + * @m2m: memory-to-memory V4L2 device information 154 + * @state: flags used to synchronize m2m and capture mode operation 155 + * @alloc_ctx: videobuf2 memory allocator context 156 + * @clock: IP clock 157 + * @regs: registers 158 + * @irq_queue: interrupt handler waitqueue 159 + * @work_queue: workqueue to handle timeouts 160 + * @timeout_work: IRQ timeout structure 161 + */ 162 + struct bdisp_dev { 163 + struct v4l2_device v4l2_dev; 164 + struct video_device vdev; 165 + struct platform_device *pdev; 166 + struct device *dev; 167 + spinlock_t slock; 168 + struct mutex lock; 169 + u16 id; 170 + struct bdisp_m2m_device m2m; 171 + unsigned long state; 172 + struct vb2_alloc_ctx *alloc_ctx; 173 + struct clk *clock; 174 + void __iomem *regs; 175 + wait_queue_head_t irq_queue; 176 + struct workqueue_struct *work_queue; 177 + struct delayed_work timeout_work; 178 + }; 179 + 180 + void bdisp_hw_free_nodes(struct bdisp_ctx *ctx); 181 + int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx); 182 + void bdisp_hw_free_filters(struct device *dev); 183 + int bdisp_hw_alloc_filters(struct device *dev); 184 + int bdisp_hw_reset(struct bdisp_dev *bdisp); 185 + int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp); 186 + int bdisp_hw_update(struct bdisp_ctx *ctx);