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

media: sun8i: Add Allwinner A83T Rotate driver

Allwinner A83T contains rotation core which can rotate and flip images.

Add a driver for it.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
[hverkuil-cisco@xs4all.nl: MAINTAINERS paths were out of date, fix that]
[hverkuil-cisco@xs4all.nl: VFL_TYPE_GRABBER -> _VIDEO]
[hverkuil-cisco@xs4all.nl: Fix module build]
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Jernej Skrabec and committed by
Mauro Carvalho Chehab
d77182ad 02fd2782

+1383
+8
MAINTAINERS
··· 14363 14363 F: include/uapi/linux/rose.h 14364 14364 F: net/rose/ 14365 14365 14366 + ROTATION DRIVER FOR ALLWINNER A83T 14367 + M: Jernej Skrabec <jernej.skrabec@siol.net> 14368 + L: linux-media@vger.kernel.org 14369 + T: git git://linuxtv.org/media_tree.git 14370 + S: Maintained 14371 + F: drivers/media/platform/sunxi/sun8i-rotate/ 14372 + F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml 14373 + 14366 14374 RTL2830 MEDIA DRIVER 14367 14375 M: Antti Palosaari <crope@iki.fi> 14368 14376 L: linux-media@vger.kernel.org
+12
drivers/media/platform/Kconfig
··· 507 507 capability found on some SoCs, like H3. 508 508 To compile this driver as a module choose m here. 509 509 510 + config VIDEO_SUN8I_ROTATE 511 + tristate "Allwinner DE2 rotation driver" 512 + depends on VIDEO_DEV && VIDEO_V4L2 513 + depends on ARCH_SUNXI || COMPILE_TEST 514 + depends on COMMON_CLK && OF 515 + depends on PM 516 + select VIDEOBUF2_DMA_CONTIG 517 + select V4L2_MEM2MEM_DEV 518 + help 519 + Support for the Allwinner DE2 rotation unit. 520 + To compile this driver as a module choose m here. 521 + 510 522 endif # V4L_MEM2MEM_DRIVERS 511 523 512 524 # TI VIDEO PORT Helper Modules
+1
drivers/media/platform/sunxi/Makefile
··· 1 1 obj-y += sun4i-csi/ 2 2 obj-y += sun6i-csi/ 3 3 obj-y += sun8i-di/ 4 + obj-y += sun8i-rotate/
+5
drivers/media/platform/sunxi/sun8i-rotate/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + sun8i-rotate-y += sun8i_rotate.o 3 + sun8i-rotate-y += sun8i_formats.o 4 + 5 + obj-$(CONFIG_VIDEO_SUN8I_ROTATE) += sun8i-rotate.o
+25
drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */ 3 + 4 + #ifndef _SUN8I_FORMATS_H_ 5 + #define _SUN8I_FORMATS_H_ 6 + 7 + #include <linux/videodev2.h> 8 + 9 + #define ROTATE_FLAG_YUV BIT(0) 10 + #define ROTATE_FLAG_OUTPUT BIT(1) 11 + 12 + struct rotate_format { 13 + u32 fourcc; 14 + u32 hw_format; 15 + int planes; 16 + int bpp[3]; 17 + int hsub; 18 + int vsub; 19 + unsigned int flags; 20 + }; 21 + 22 + const struct rotate_format *rotate_find_format(u32 pixelformat); 23 + int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst); 24 + 25 + #endif
+135
drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Allwinner DE2 rotation driver 4 + * 5 + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> 6 + */ 7 + 8 + #ifndef _SUN8I_ROTATE_H_ 9 + #define _SUN8I_ROTATE_H_ 10 + 11 + #include <media/v4l2-ctrls.h> 12 + #include <media/v4l2-device.h> 13 + #include <media/v4l2-mem2mem.h> 14 + #include <media/videobuf2-v4l2.h> 15 + #include <media/videobuf2-dma-contig.h> 16 + 17 + #include <linux/platform_device.h> 18 + 19 + #define ROTATE_NAME "sun8i-rotate" 20 + 21 + #define ROTATE_GLB_CTL 0x00 22 + #define ROTATE_GLB_CTL_START BIT(31) 23 + #define ROTATE_GLB_CTL_RESET BIT(30) 24 + #define ROTATE_GLB_CTL_BURST_LEN(x) ((x) << 16) 25 + #define ROTATE_GLB_CTL_HFLIP BIT(7) 26 + #define ROTATE_GLB_CTL_VFLIP BIT(6) 27 + #define ROTATE_GLB_CTL_ROTATION(x) ((x) << 4) 28 + #define ROTATE_GLB_CTL_MODE(x) ((x) << 0) 29 + 30 + #define ROTATE_INT 0x04 31 + #define ROTATE_INT_FINISH_IRQ_EN BIT(16) 32 + #define ROTATE_INT_FINISH_IRQ BIT(0) 33 + 34 + #define ROTATE_IN_FMT 0x20 35 + #define ROTATE_IN_FMT_FORMAT(x) ((x) << 0) 36 + 37 + #define ROTATE_IN_SIZE 0x24 38 + #define ROTATE_IN_PITCH0 0x30 39 + #define ROTATE_IN_PITCH1 0x34 40 + #define ROTATE_IN_PITCH2 0x38 41 + #define ROTATE_IN_ADDRL0 0x40 42 + #define ROTATE_IN_ADDRH0 0x44 43 + #define ROTATE_IN_ADDRL1 0x48 44 + #define ROTATE_IN_ADDRH1 0x4c 45 + #define ROTATE_IN_ADDRL2 0x50 46 + #define ROTATE_IN_ADDRH2 0x54 47 + #define ROTATE_OUT_SIZE 0x84 48 + #define ROTATE_OUT_PITCH0 0x90 49 + #define ROTATE_OUT_PITCH1 0x94 50 + #define ROTATE_OUT_PITCH2 0x98 51 + #define ROTATE_OUT_ADDRL0 0xA0 52 + #define ROTATE_OUT_ADDRH0 0xA4 53 + #define ROTATE_OUT_ADDRL1 0xA8 54 + #define ROTATE_OUT_ADDRH1 0xAC 55 + #define ROTATE_OUT_ADDRL2 0xB0 56 + #define ROTATE_OUT_ADDRH2 0xB4 57 + 58 + #define ROTATE_BURST_8 0x07 59 + #define ROTATE_BURST_16 0x0f 60 + #define ROTATE_BURST_32 0x1f 61 + #define ROTATE_BURST_64 0x3f 62 + 63 + #define ROTATE_MODE_COPY_ROTATE 0x01 64 + 65 + #define ROTATE_FORMAT_ARGB32 0x00 66 + #define ROTATE_FORMAT_ABGR32 0x01 67 + #define ROTATE_FORMAT_RGBA32 0x02 68 + #define ROTATE_FORMAT_BGRA32 0x03 69 + #define ROTATE_FORMAT_XRGB32 0x04 70 + #define ROTATE_FORMAT_XBGR32 0x05 71 + #define ROTATE_FORMAT_RGBX32 0x06 72 + #define ROTATE_FORMAT_BGRX32 0x07 73 + #define ROTATE_FORMAT_RGB24 0x08 74 + #define ROTATE_FORMAT_BGR24 0x09 75 + #define ROTATE_FORMAT_RGB565 0x0a 76 + #define ROTATE_FORMAT_BGR565 0x0b 77 + #define ROTATE_FORMAT_ARGB4444 0x0c 78 + #define ROTATE_FORMAT_ABGR4444 0x0d 79 + #define ROTATE_FORMAT_RGBA4444 0x0e 80 + #define ROTATE_FORMAT_BGRA4444 0x0f 81 + #define ROTATE_FORMAT_ARGB1555 0x10 82 + #define ROTATE_FORMAT_ABGR1555 0x11 83 + #define ROTATE_FORMAT_RGBA5551 0x12 84 + #define ROTATE_FORMAT_BGRA5551 0x13 85 + 86 + #define ROTATE_FORMAT_YUYV 0x20 87 + #define ROTATE_FORMAT_UYVY 0x21 88 + #define ROTATE_FORMAT_YVYU 0x22 89 + #define ROTATE_FORMAT_VYUV 0x23 90 + #define ROTATE_FORMAT_NV61 0x24 91 + #define ROTATE_FORMAT_NV16 0x25 92 + #define ROTATE_FORMAT_YUV422P 0x26 93 + #define ROTATE_FORMAT_NV21 0x28 94 + #define ROTATE_FORMAT_NV12 0x29 95 + #define ROTATE_FORMAT_YUV420P 0x2A 96 + 97 + #define ROTATE_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) 98 + 99 + #define ROTATE_MIN_WIDTH 8U 100 + #define ROTATE_MIN_HEIGHT 8U 101 + #define ROTATE_MAX_WIDTH 4096U 102 + #define ROTATE_MAX_HEIGHT 4096U 103 + 104 + struct rotate_ctx { 105 + struct v4l2_fh fh; 106 + struct rotate_dev *dev; 107 + 108 + struct v4l2_pix_format src_fmt; 109 + struct v4l2_pix_format dst_fmt; 110 + 111 + struct v4l2_ctrl_handler ctrl_handler; 112 + 113 + u32 hflip; 114 + u32 vflip; 115 + u32 rotate; 116 + }; 117 + 118 + struct rotate_dev { 119 + struct v4l2_device v4l2_dev; 120 + struct video_device vfd; 121 + struct device *dev; 122 + struct v4l2_m2m_dev *m2m_dev; 123 + 124 + /* Device file mutex */ 125 + struct mutex dev_mutex; 126 + 127 + void __iomem *base; 128 + 129 + struct clk *bus_clk; 130 + struct clk *mod_clk; 131 + 132 + struct reset_control *rstc; 133 + }; 134 + 135 + #endif
+273
drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */ 3 + 4 + #include "sun8i-formats.h" 5 + #include "sun8i-rotate.h" 6 + 7 + /* 8 + * Formats not included in array: 9 + * ROTATE_FORMAT_BGR565 10 + * ROTATE_FORMAT_VYUV 11 + */ 12 + 13 + static const struct rotate_format rotate_formats[] = { 14 + { 15 + .fourcc = V4L2_PIX_FMT_ARGB32, 16 + .hw_format = ROTATE_FORMAT_ARGB32, 17 + .planes = 1, 18 + .bpp = { 4, 0, 0 }, 19 + .hsub = 1, 20 + .vsub = 1, 21 + .flags = ROTATE_FLAG_OUTPUT 22 + }, { 23 + .fourcc = V4L2_PIX_FMT_ABGR32, 24 + .hw_format = ROTATE_FORMAT_ABGR32, 25 + .planes = 1, 26 + .bpp = { 4, 0, 0 }, 27 + .hsub = 1, 28 + .vsub = 1, 29 + .flags = ROTATE_FLAG_OUTPUT 30 + }, { 31 + .fourcc = V4L2_PIX_FMT_RGBA32, 32 + .hw_format = ROTATE_FORMAT_RGBA32, 33 + .planes = 1, 34 + .bpp = { 4, 0, 0 }, 35 + .hsub = 1, 36 + .vsub = 1, 37 + .flags = ROTATE_FLAG_OUTPUT 38 + }, { 39 + .fourcc = V4L2_PIX_FMT_BGRA32, 40 + .hw_format = ROTATE_FORMAT_BGRA32, 41 + .planes = 1, 42 + .bpp = { 4, 0, 0 }, 43 + .hsub = 1, 44 + .vsub = 1, 45 + .flags = ROTATE_FLAG_OUTPUT 46 + }, { 47 + .fourcc = V4L2_PIX_FMT_XRGB32, 48 + .hw_format = ROTATE_FORMAT_XRGB32, 49 + .planes = 1, 50 + .bpp = { 4, 0, 0 }, 51 + .hsub = 1, 52 + .vsub = 1, 53 + .flags = ROTATE_FLAG_OUTPUT 54 + }, { 55 + .fourcc = V4L2_PIX_FMT_XBGR32, 56 + .hw_format = ROTATE_FORMAT_XBGR32, 57 + .planes = 1, 58 + .bpp = { 4, 0, 0 }, 59 + .hsub = 1, 60 + .vsub = 1, 61 + .flags = ROTATE_FLAG_OUTPUT 62 + }, { 63 + .fourcc = V4L2_PIX_FMT_RGB32, 64 + .hw_format = ROTATE_FORMAT_RGBX32, 65 + .planes = 1, 66 + .bpp = { 4, 0, 0 }, 67 + .hsub = 1, 68 + .vsub = 1, 69 + .flags = ROTATE_FLAG_OUTPUT 70 + }, { 71 + .fourcc = V4L2_PIX_FMT_BGR32, 72 + .hw_format = ROTATE_FORMAT_BGRX32, 73 + .planes = 1, 74 + .bpp = { 4, 0, 0 }, 75 + .hsub = 1, 76 + .vsub = 1, 77 + .flags = ROTATE_FLAG_OUTPUT 78 + }, { 79 + .fourcc = V4L2_PIX_FMT_RGB24, 80 + .hw_format = ROTATE_FORMAT_RGB24, 81 + .planes = 1, 82 + .bpp = { 3, 0, 0 }, 83 + .hsub = 1, 84 + .vsub = 1, 85 + .flags = ROTATE_FLAG_OUTPUT 86 + }, { 87 + .fourcc = V4L2_PIX_FMT_BGR24, 88 + .hw_format = ROTATE_FORMAT_BGR24, 89 + .planes = 1, 90 + .bpp = { 3, 0, 0 }, 91 + .hsub = 1, 92 + .vsub = 1, 93 + .flags = ROTATE_FLAG_OUTPUT 94 + }, { 95 + .fourcc = V4L2_PIX_FMT_RGB565, 96 + .hw_format = ROTATE_FORMAT_RGB565, 97 + .planes = 1, 98 + .bpp = { 2, 0, 0 }, 99 + .hsub = 1, 100 + .vsub = 1, 101 + .flags = ROTATE_FLAG_OUTPUT 102 + }, { 103 + .fourcc = V4L2_PIX_FMT_ARGB444, 104 + .hw_format = ROTATE_FORMAT_ARGB4444, 105 + .planes = 1, 106 + .bpp = { 2, 0, 0 }, 107 + .hsub = 1, 108 + .vsub = 1, 109 + .flags = ROTATE_FLAG_OUTPUT 110 + }, { 111 + .fourcc = V4L2_PIX_FMT_ABGR444, 112 + .hw_format = ROTATE_FORMAT_ABGR4444, 113 + .planes = 1, 114 + .bpp = { 2, 0, 0 }, 115 + .hsub = 1, 116 + .vsub = 1, 117 + .flags = ROTATE_FLAG_OUTPUT 118 + }, { 119 + .fourcc = V4L2_PIX_FMT_RGBA444, 120 + .hw_format = ROTATE_FORMAT_RGBA4444, 121 + .planes = 1, 122 + .bpp = { 2, 0, 0 }, 123 + .hsub = 1, 124 + .vsub = 1, 125 + .flags = ROTATE_FLAG_OUTPUT 126 + }, { 127 + .fourcc = V4L2_PIX_FMT_BGRA444, 128 + .hw_format = ROTATE_FORMAT_BGRA4444, 129 + .planes = 1, 130 + .bpp = { 2, 0, 0 }, 131 + .hsub = 1, 132 + .vsub = 1, 133 + .flags = ROTATE_FLAG_OUTPUT 134 + }, { 135 + .fourcc = V4L2_PIX_FMT_ARGB555, 136 + .hw_format = ROTATE_FORMAT_ARGB1555, 137 + .planes = 1, 138 + .bpp = { 2, 0, 0 }, 139 + .hsub = 1, 140 + .vsub = 1, 141 + .flags = ROTATE_FLAG_OUTPUT 142 + }, { 143 + .fourcc = V4L2_PIX_FMT_ABGR555, 144 + .hw_format = ROTATE_FORMAT_ABGR1555, 145 + .planes = 1, 146 + .bpp = { 2, 0, 0 }, 147 + .hsub = 1, 148 + .vsub = 1, 149 + .flags = ROTATE_FLAG_OUTPUT 150 + }, { 151 + .fourcc = V4L2_PIX_FMT_RGBA555, 152 + .hw_format = ROTATE_FORMAT_RGBA5551, 153 + .planes = 1, 154 + .bpp = { 2, 0, 0 }, 155 + .hsub = 1, 156 + .vsub = 1, 157 + .flags = ROTATE_FLAG_OUTPUT 158 + }, { 159 + .fourcc = V4L2_PIX_FMT_BGRA555, 160 + .hw_format = ROTATE_FORMAT_BGRA5551, 161 + .planes = 1, 162 + .bpp = { 2, 0, 0 }, 163 + .hsub = 1, 164 + .vsub = 1, 165 + .flags = ROTATE_FLAG_OUTPUT 166 + }, { 167 + .fourcc = V4L2_PIX_FMT_YVYU, 168 + .hw_format = ROTATE_FORMAT_YVYU, 169 + .planes = 1, 170 + .bpp = { 2, 0, 0 }, 171 + .hsub = 2, 172 + .vsub = 1, 173 + .flags = ROTATE_FLAG_YUV 174 + }, { 175 + .fourcc = V4L2_PIX_FMT_UYVY, 176 + .hw_format = ROTATE_FORMAT_UYVY, 177 + .planes = 1, 178 + .bpp = { 2, 0, 0 }, 179 + .hsub = 2, 180 + .vsub = 1, 181 + .flags = ROTATE_FLAG_YUV 182 + }, { 183 + .fourcc = V4L2_PIX_FMT_YUYV, 184 + .hw_format = ROTATE_FORMAT_YUYV, 185 + .planes = 1, 186 + .bpp = { 2, 0, 0 }, 187 + .hsub = 2, 188 + .vsub = 1, 189 + .flags = ROTATE_FLAG_YUV 190 + }, { 191 + .fourcc = V4L2_PIX_FMT_NV61, 192 + .hw_format = ROTATE_FORMAT_NV61, 193 + .planes = 2, 194 + .bpp = { 1, 2, 0 }, 195 + .hsub = 2, 196 + .vsub = 1, 197 + .flags = ROTATE_FLAG_YUV 198 + }, { 199 + .fourcc = V4L2_PIX_FMT_NV16, 200 + .hw_format = ROTATE_FORMAT_NV16, 201 + .planes = 2, 202 + .bpp = { 1, 2, 0 }, 203 + .hsub = 2, 204 + .vsub = 1, 205 + .flags = ROTATE_FLAG_YUV 206 + }, { 207 + .fourcc = V4L2_PIX_FMT_YUV422P, 208 + .hw_format = ROTATE_FORMAT_YUV422P, 209 + .planes = 3, 210 + .bpp = { 1, 1, 1 }, 211 + .hsub = 2, 212 + .vsub = 1, 213 + .flags = ROTATE_FLAG_YUV 214 + }, { 215 + .fourcc = V4L2_PIX_FMT_NV21, 216 + .hw_format = ROTATE_FORMAT_NV21, 217 + .planes = 2, 218 + .bpp = { 1, 2, 0 }, 219 + .hsub = 2, 220 + .vsub = 2, 221 + .flags = ROTATE_FLAG_YUV 222 + }, { 223 + .fourcc = V4L2_PIX_FMT_NV12, 224 + .hw_format = ROTATE_FORMAT_NV12, 225 + .planes = 2, 226 + .bpp = { 1, 2, 0 }, 227 + .hsub = 2, 228 + .vsub = 2, 229 + .flags = ROTATE_FLAG_YUV 230 + }, { 231 + .fourcc = V4L2_PIX_FMT_YUV420, 232 + .hw_format = ROTATE_FORMAT_YUV420P, 233 + .planes = 3, 234 + .bpp = { 1, 1, 1 }, 235 + .hsub = 2, 236 + .vsub = 2, 237 + .flags = ROTATE_FLAG_YUV | ROTATE_FLAG_OUTPUT 238 + }, 239 + }; 240 + 241 + const struct rotate_format *rotate_find_format(u32 pixelformat) 242 + { 243 + unsigned int i; 244 + 245 + for (i = 0; i < ARRAY_SIZE(rotate_formats); i++) 246 + if (rotate_formats[i].fourcc == pixelformat) 247 + return &rotate_formats[i]; 248 + 249 + return NULL; 250 + } 251 + 252 + int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst) 253 + { 254 + int i, index; 255 + 256 + index = 0; 257 + 258 + for (i = 0; i < ARRAY_SIZE(rotate_formats); i++) { 259 + /* not all formats can be used for capture buffers */ 260 + if (dst && !(rotate_formats[i].flags & ROTATE_FLAG_OUTPUT)) 261 + continue; 262 + 263 + if (index == f->index) { 264 + f->pixelformat = rotate_formats[i].fourcc; 265 + 266 + return 0; 267 + } 268 + 269 + index++; 270 + } 271 + 272 + return -EINVAL; 273 + }
+924
drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Allwinner sun8i DE2 rotation driver 4 + * 5 + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/io.h> 11 + #include <linux/iopoll.h> 12 + #include <linux/module.h> 13 + #include <linux/of.h> 14 + #include <linux/of_device.h> 15 + #include <linux/pm_runtime.h> 16 + #include <linux/reset.h> 17 + 18 + #include <media/v4l2-device.h> 19 + #include <media/v4l2-event.h> 20 + #include <media/v4l2-ioctl.h> 21 + #include <media/v4l2-mem2mem.h> 22 + 23 + #include "sun8i-formats.h" 24 + #include "sun8i-rotate.h" 25 + 26 + static inline u32 rotate_read(struct rotate_dev *dev, u32 reg) 27 + { 28 + return readl(dev->base + reg); 29 + } 30 + 31 + static inline void rotate_write(struct rotate_dev *dev, u32 reg, u32 value) 32 + { 33 + writel(value, dev->base + reg); 34 + } 35 + 36 + static inline void rotate_set_bits(struct rotate_dev *dev, u32 reg, u32 bits) 37 + { 38 + writel(readl(dev->base + reg) | bits, dev->base + reg); 39 + } 40 + 41 + static void rotate_calc_addr_pitch(dma_addr_t buffer, 42 + u32 bytesperline, u32 height, 43 + const struct rotate_format *fmt, 44 + dma_addr_t *addr, u32 *pitch) 45 + { 46 + u32 size; 47 + int i; 48 + 49 + for (i = 0; i < fmt->planes; i++) { 50 + pitch[i] = bytesperline; 51 + addr[i] = buffer; 52 + if (i > 0) 53 + pitch[i] /= fmt->hsub / fmt->bpp[i]; 54 + size = pitch[i] * height; 55 + if (i > 0) 56 + size /= fmt->vsub; 57 + buffer += size; 58 + } 59 + } 60 + 61 + static void rotate_device_run(void *priv) 62 + { 63 + struct rotate_ctx *ctx = priv; 64 + struct rotate_dev *dev = ctx->dev; 65 + struct vb2_v4l2_buffer *src, *dst; 66 + const struct rotate_format *fmt; 67 + dma_addr_t addr[3]; 68 + u32 val, pitch[3]; 69 + 70 + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 71 + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 72 + 73 + v4l2_m2m_buf_copy_metadata(src, dst, true); 74 + 75 + val = ROTATE_GLB_CTL_MODE(ROTATE_MODE_COPY_ROTATE); 76 + if (ctx->hflip) 77 + val |= ROTATE_GLB_CTL_HFLIP; 78 + if (ctx->vflip) 79 + val |= ROTATE_GLB_CTL_VFLIP; 80 + val |= ROTATE_GLB_CTL_ROTATION(ctx->rotate / 90); 81 + if (ctx->rotate != 90 && ctx->rotate != 270) 82 + val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_64); 83 + else 84 + val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_8); 85 + rotate_write(dev, ROTATE_GLB_CTL, val); 86 + 87 + fmt = rotate_find_format(ctx->src_fmt.pixelformat); 88 + if (!fmt) 89 + return; 90 + 91 + rotate_write(dev, ROTATE_IN_FMT, ROTATE_IN_FMT_FORMAT(fmt->hw_format)); 92 + 93 + rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0), 94 + ctx->src_fmt.bytesperline, ctx->src_fmt.height, 95 + fmt, addr, pitch); 96 + 97 + rotate_write(dev, ROTATE_IN_SIZE, 98 + ROTATE_SIZE(ctx->src_fmt.width, ctx->src_fmt.height)); 99 + 100 + rotate_write(dev, ROTATE_IN_PITCH0, pitch[0]); 101 + rotate_write(dev, ROTATE_IN_PITCH1, pitch[1]); 102 + rotate_write(dev, ROTATE_IN_PITCH2, pitch[2]); 103 + 104 + rotate_write(dev, ROTATE_IN_ADDRL0, addr[0]); 105 + rotate_write(dev, ROTATE_IN_ADDRL1, addr[1]); 106 + rotate_write(dev, ROTATE_IN_ADDRL2, addr[2]); 107 + 108 + rotate_write(dev, ROTATE_IN_ADDRH0, 0); 109 + rotate_write(dev, ROTATE_IN_ADDRH1, 0); 110 + rotate_write(dev, ROTATE_IN_ADDRH2, 0); 111 + 112 + fmt = rotate_find_format(ctx->dst_fmt.pixelformat); 113 + if (!fmt) 114 + return; 115 + 116 + rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0), 117 + ctx->dst_fmt.bytesperline, ctx->dst_fmt.height, 118 + fmt, addr, pitch); 119 + 120 + rotate_write(dev, ROTATE_OUT_SIZE, 121 + ROTATE_SIZE(ctx->dst_fmt.width, ctx->dst_fmt.height)); 122 + 123 + rotate_write(dev, ROTATE_OUT_PITCH0, pitch[0]); 124 + rotate_write(dev, ROTATE_OUT_PITCH1, pitch[1]); 125 + rotate_write(dev, ROTATE_OUT_PITCH2, pitch[2]); 126 + 127 + rotate_write(dev, ROTATE_OUT_ADDRL0, addr[0]); 128 + rotate_write(dev, ROTATE_OUT_ADDRL1, addr[1]); 129 + rotate_write(dev, ROTATE_OUT_ADDRL2, addr[2]); 130 + 131 + rotate_write(dev, ROTATE_OUT_ADDRH0, 0); 132 + rotate_write(dev, ROTATE_OUT_ADDRH1, 0); 133 + rotate_write(dev, ROTATE_OUT_ADDRH2, 0); 134 + 135 + rotate_set_bits(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ_EN); 136 + rotate_set_bits(dev, ROTATE_GLB_CTL, ROTATE_GLB_CTL_START); 137 + } 138 + 139 + static irqreturn_t rotate_irq(int irq, void *data) 140 + { 141 + struct vb2_v4l2_buffer *buffer; 142 + struct rotate_dev *dev = data; 143 + struct rotate_ctx *ctx; 144 + unsigned int val; 145 + 146 + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); 147 + if (!ctx) { 148 + v4l2_err(&dev->v4l2_dev, 149 + "Instance released before the end of transaction\n"); 150 + return IRQ_NONE; 151 + } 152 + 153 + val = rotate_read(dev, ROTATE_INT); 154 + if (!(val & ROTATE_INT_FINISH_IRQ)) 155 + return IRQ_NONE; 156 + 157 + /* clear flag and disable irq */ 158 + rotate_write(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ); 159 + 160 + buffer = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 161 + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE); 162 + 163 + buffer = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 164 + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE); 165 + 166 + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); 167 + 168 + return IRQ_HANDLED; 169 + } 170 + 171 + static inline struct rotate_ctx *rotate_file2ctx(struct file *file) 172 + { 173 + return container_of(file->private_data, struct rotate_ctx, fh); 174 + } 175 + 176 + static void rotate_prepare_format(struct v4l2_pix_format *pix_fmt) 177 + { 178 + unsigned int height, width, alignment, sizeimage, size, bpl; 179 + const struct rotate_format *fmt; 180 + int i; 181 + 182 + fmt = rotate_find_format(pix_fmt->pixelformat); 183 + if (!fmt) 184 + return; 185 + 186 + width = ALIGN(pix_fmt->width, fmt->hsub); 187 + height = ALIGN(pix_fmt->height, fmt->vsub); 188 + 189 + /* all pitches have to be 16 byte aligned */ 190 + alignment = 16; 191 + if (fmt->planes > 1) 192 + alignment *= fmt->hsub / fmt->bpp[1]; 193 + bpl = ALIGN(width * fmt->bpp[0], alignment); 194 + 195 + sizeimage = 0; 196 + for (i = 0; i < fmt->planes; i++) { 197 + size = bpl * height; 198 + if (i > 0) { 199 + size *= fmt->bpp[i]; 200 + size /= fmt->hsub; 201 + size /= fmt->vsub; 202 + } 203 + sizeimage += size; 204 + } 205 + 206 + pix_fmt->width = width; 207 + pix_fmt->height = height; 208 + pix_fmt->bytesperline = bpl; 209 + pix_fmt->sizeimage = sizeimage; 210 + } 211 + 212 + static int rotate_querycap(struct file *file, void *priv, 213 + struct v4l2_capability *cap) 214 + { 215 + strscpy(cap->driver, ROTATE_NAME, sizeof(cap->driver)); 216 + strscpy(cap->card, ROTATE_NAME, sizeof(cap->card)); 217 + snprintf(cap->bus_info, sizeof(cap->bus_info), 218 + "platform:%s", ROTATE_NAME); 219 + 220 + return 0; 221 + } 222 + 223 + static int rotate_enum_fmt_vid_cap(struct file *file, void *priv, 224 + struct v4l2_fmtdesc *f) 225 + { 226 + return rotate_enum_fmt(f, true); 227 + } 228 + 229 + static int rotate_enum_fmt_vid_out(struct file *file, void *priv, 230 + struct v4l2_fmtdesc *f) 231 + { 232 + return rotate_enum_fmt(f, false); 233 + } 234 + 235 + static int rotate_enum_framesizes(struct file *file, void *priv, 236 + struct v4l2_frmsizeenum *fsize) 237 + { 238 + const struct rotate_format *fmt; 239 + 240 + if (fsize->index != 0) 241 + return -EINVAL; 242 + 243 + fmt = rotate_find_format(fsize->pixel_format); 244 + if (!fmt) 245 + return -EINVAL; 246 + 247 + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 248 + fsize->stepwise.min_width = ROTATE_MIN_WIDTH; 249 + fsize->stepwise.min_height = ROTATE_MIN_HEIGHT; 250 + fsize->stepwise.max_width = ROTATE_MAX_WIDTH; 251 + fsize->stepwise.max_height = ROTATE_MAX_HEIGHT; 252 + fsize->stepwise.step_width = fmt->hsub; 253 + fsize->stepwise.step_height = fmt->vsub; 254 + 255 + return 0; 256 + } 257 + 258 + static int rotate_set_cap_format(struct rotate_ctx *ctx, 259 + struct v4l2_pix_format *f, 260 + u32 rotate) 261 + { 262 + const struct rotate_format *fmt; 263 + 264 + fmt = rotate_find_format(ctx->src_fmt.pixelformat); 265 + if (!fmt) 266 + return -EINVAL; 267 + 268 + if (fmt->flags & ROTATE_FLAG_YUV) 269 + f->pixelformat = V4L2_PIX_FMT_YUV420; 270 + else 271 + f->pixelformat = ctx->src_fmt.pixelformat; 272 + 273 + f->field = V4L2_FIELD_NONE; 274 + 275 + if (rotate == 90 || rotate == 270) { 276 + f->width = ctx->src_fmt.height; 277 + f->height = ctx->src_fmt.width; 278 + } else { 279 + f->width = ctx->src_fmt.width; 280 + f->height = ctx->src_fmt.height; 281 + } 282 + 283 + rotate_prepare_format(f); 284 + 285 + return 0; 286 + } 287 + 288 + static int rotate_g_fmt_vid_cap(struct file *file, void *priv, 289 + struct v4l2_format *f) 290 + { 291 + struct rotate_ctx *ctx = rotate_file2ctx(file); 292 + 293 + f->fmt.pix = ctx->dst_fmt; 294 + 295 + return 0; 296 + } 297 + 298 + static int rotate_g_fmt_vid_out(struct file *file, void *priv, 299 + struct v4l2_format *f) 300 + { 301 + struct rotate_ctx *ctx = rotate_file2ctx(file); 302 + 303 + f->fmt.pix = ctx->src_fmt; 304 + 305 + return 0; 306 + } 307 + 308 + static int rotate_try_fmt_vid_cap(struct file *file, void *priv, 309 + struct v4l2_format *f) 310 + { 311 + struct rotate_ctx *ctx = rotate_file2ctx(file); 312 + 313 + return rotate_set_cap_format(ctx, &f->fmt.pix, ctx->rotate); 314 + } 315 + 316 + static int rotate_try_fmt_vid_out(struct file *file, void *priv, 317 + struct v4l2_format *f) 318 + { 319 + if (!rotate_find_format(f->fmt.pix.pixelformat)) 320 + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ARGB32; 321 + 322 + if (f->fmt.pix.width < ROTATE_MIN_WIDTH) 323 + f->fmt.pix.width = ROTATE_MIN_WIDTH; 324 + if (f->fmt.pix.height < ROTATE_MIN_HEIGHT) 325 + f->fmt.pix.height = ROTATE_MIN_HEIGHT; 326 + 327 + if (f->fmt.pix.width > ROTATE_MAX_WIDTH) 328 + f->fmt.pix.width = ROTATE_MAX_WIDTH; 329 + if (f->fmt.pix.height > ROTATE_MAX_HEIGHT) 330 + f->fmt.pix.height = ROTATE_MAX_HEIGHT; 331 + 332 + f->fmt.pix.field = V4L2_FIELD_NONE; 333 + 334 + rotate_prepare_format(&f->fmt.pix); 335 + 336 + return 0; 337 + } 338 + 339 + static int rotate_s_fmt_vid_cap(struct file *file, void *priv, 340 + struct v4l2_format *f) 341 + { 342 + struct rotate_ctx *ctx = rotate_file2ctx(file); 343 + struct vb2_queue *vq; 344 + int ret; 345 + 346 + ret = rotate_try_fmt_vid_cap(file, priv, f); 347 + if (ret) 348 + return ret; 349 + 350 + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 351 + if (vb2_is_busy(vq)) 352 + return -EBUSY; 353 + 354 + ctx->dst_fmt = f->fmt.pix; 355 + 356 + return 0; 357 + } 358 + 359 + static int rotate_s_fmt_vid_out(struct file *file, void *priv, 360 + struct v4l2_format *f) 361 + { 362 + struct rotate_ctx *ctx = rotate_file2ctx(file); 363 + struct vb2_queue *vq; 364 + int ret; 365 + 366 + ret = rotate_try_fmt_vid_out(file, priv, f); 367 + if (ret) 368 + return ret; 369 + 370 + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 371 + if (vb2_is_busy(vq)) 372 + return -EBUSY; 373 + 374 + /* 375 + * Capture queue has to be also checked, because format and size 376 + * depends on output format and size. 377 + */ 378 + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 379 + if (vb2_is_busy(vq)) 380 + return -EBUSY; 381 + 382 + ctx->src_fmt = f->fmt.pix; 383 + 384 + /* Propagate colorspace information to capture. */ 385 + ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; 386 + ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; 387 + ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; 388 + ctx->dst_fmt.quantization = f->fmt.pix.quantization; 389 + 390 + return rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate); 391 + } 392 + 393 + static const struct v4l2_ioctl_ops rotate_ioctl_ops = { 394 + .vidioc_querycap = rotate_querycap, 395 + 396 + .vidioc_enum_framesizes = rotate_enum_framesizes, 397 + 398 + .vidioc_enum_fmt_vid_cap = rotate_enum_fmt_vid_cap, 399 + .vidioc_g_fmt_vid_cap = rotate_g_fmt_vid_cap, 400 + .vidioc_try_fmt_vid_cap = rotate_try_fmt_vid_cap, 401 + .vidioc_s_fmt_vid_cap = rotate_s_fmt_vid_cap, 402 + 403 + .vidioc_enum_fmt_vid_out = rotate_enum_fmt_vid_out, 404 + .vidioc_g_fmt_vid_out = rotate_g_fmt_vid_out, 405 + .vidioc_try_fmt_vid_out = rotate_try_fmt_vid_out, 406 + .vidioc_s_fmt_vid_out = rotate_s_fmt_vid_out, 407 + 408 + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 409 + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 410 + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 411 + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 412 + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 413 + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 414 + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 415 + 416 + .vidioc_streamon = v4l2_m2m_ioctl_streamon, 417 + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 418 + 419 + .vidioc_log_status = v4l2_ctrl_log_status, 420 + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 421 + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 422 + }; 423 + 424 + static int rotate_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 425 + unsigned int *nplanes, unsigned int sizes[], 426 + struct device *alloc_devs[]) 427 + { 428 + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); 429 + struct v4l2_pix_format *pix_fmt; 430 + 431 + if (V4L2_TYPE_IS_OUTPUT(vq->type)) 432 + pix_fmt = &ctx->src_fmt; 433 + else 434 + pix_fmt = &ctx->dst_fmt; 435 + 436 + if (*nplanes) { 437 + if (sizes[0] < pix_fmt->sizeimage) 438 + return -EINVAL; 439 + } else { 440 + sizes[0] = pix_fmt->sizeimage; 441 + *nplanes = 1; 442 + } 443 + 444 + return 0; 445 + } 446 + 447 + static int rotate_buf_prepare(struct vb2_buffer *vb) 448 + { 449 + struct vb2_queue *vq = vb->vb2_queue; 450 + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); 451 + struct v4l2_pix_format *pix_fmt; 452 + 453 + if (V4L2_TYPE_IS_OUTPUT(vq->type)) 454 + pix_fmt = &ctx->src_fmt; 455 + else 456 + pix_fmt = &ctx->dst_fmt; 457 + 458 + if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) 459 + return -EINVAL; 460 + 461 + vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); 462 + 463 + return 0; 464 + } 465 + 466 + static void rotate_buf_queue(struct vb2_buffer *vb) 467 + { 468 + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 469 + struct rotate_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 470 + 471 + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 472 + } 473 + 474 + static void rotate_queue_cleanup(struct vb2_queue *vq, u32 state) 475 + { 476 + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); 477 + struct vb2_v4l2_buffer *vbuf; 478 + 479 + do { 480 + if (V4L2_TYPE_IS_OUTPUT(vq->type)) 481 + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 482 + else 483 + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 484 + 485 + if (vbuf) 486 + v4l2_m2m_buf_done(vbuf, state); 487 + } while (vbuf); 488 + } 489 + 490 + static int rotate_start_streaming(struct vb2_queue *vq, unsigned int count) 491 + { 492 + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 493 + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); 494 + struct device *dev = ctx->dev->dev; 495 + int ret; 496 + 497 + ret = pm_runtime_get_sync(dev); 498 + if (ret < 0) { 499 + dev_err(dev, "Failed to enable module\n"); 500 + 501 + return ret; 502 + } 503 + } 504 + 505 + return 0; 506 + } 507 + 508 + static void rotate_stop_streaming(struct vb2_queue *vq) 509 + { 510 + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { 511 + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); 512 + 513 + pm_runtime_put(ctx->dev->dev); 514 + } 515 + 516 + rotate_queue_cleanup(vq, VB2_BUF_STATE_ERROR); 517 + } 518 + 519 + static const struct vb2_ops rotate_qops = { 520 + .queue_setup = rotate_queue_setup, 521 + .buf_prepare = rotate_buf_prepare, 522 + .buf_queue = rotate_buf_queue, 523 + .start_streaming = rotate_start_streaming, 524 + .stop_streaming = rotate_stop_streaming, 525 + .wait_prepare = vb2_ops_wait_prepare, 526 + .wait_finish = vb2_ops_wait_finish, 527 + }; 528 + 529 + static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, 530 + struct vb2_queue *dst_vq) 531 + { 532 + struct rotate_ctx *ctx = priv; 533 + int ret; 534 + 535 + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 536 + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 537 + src_vq->drv_priv = ctx; 538 + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 539 + src_vq->min_buffers_needed = 1; 540 + src_vq->ops = &rotate_qops; 541 + src_vq->mem_ops = &vb2_dma_contig_memops; 542 + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 543 + src_vq->lock = &ctx->dev->dev_mutex; 544 + src_vq->dev = ctx->dev->dev; 545 + 546 + ret = vb2_queue_init(src_vq); 547 + if (ret) 548 + return ret; 549 + 550 + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 551 + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 552 + dst_vq->drv_priv = ctx; 553 + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 554 + dst_vq->min_buffers_needed = 2; 555 + dst_vq->ops = &rotate_qops; 556 + dst_vq->mem_ops = &vb2_dma_contig_memops; 557 + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 558 + dst_vq->lock = &ctx->dev->dev_mutex; 559 + dst_vq->dev = ctx->dev->dev; 560 + 561 + ret = vb2_queue_init(dst_vq); 562 + if (ret) 563 + return ret; 564 + 565 + return 0; 566 + } 567 + 568 + static int rotate_s_ctrl(struct v4l2_ctrl *ctrl) 569 + { 570 + struct rotate_ctx *ctx = container_of(ctrl->handler, 571 + struct rotate_ctx, 572 + ctrl_handler); 573 + struct v4l2_pix_format fmt; 574 + 575 + switch (ctrl->id) { 576 + case V4L2_CID_HFLIP: 577 + ctx->hflip = ctrl->val; 578 + break; 579 + case V4L2_CID_VFLIP: 580 + ctx->vflip = ctrl->val; 581 + break; 582 + case V4L2_CID_ROTATE: 583 + rotate_set_cap_format(ctx, &fmt, ctrl->val); 584 + 585 + /* Check if capture format needs to be changed */ 586 + if (fmt.width != ctx->dst_fmt.width || 587 + fmt.height != ctx->dst_fmt.height || 588 + fmt.bytesperline != ctx->dst_fmt.bytesperline || 589 + fmt.sizeimage != ctx->dst_fmt.sizeimage) { 590 + struct vb2_queue *vq; 591 + 592 + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 593 + V4L2_BUF_TYPE_VIDEO_CAPTURE); 594 + if (vb2_is_busy(vq)) 595 + return -EBUSY; 596 + 597 + rotate_set_cap_format(ctx, &ctx->dst_fmt, ctrl->val); 598 + } 599 + 600 + ctx->rotate = ctrl->val; 601 + break; 602 + default: 603 + return -EINVAL; 604 + } 605 + 606 + return 0; 607 + } 608 + 609 + static const struct v4l2_ctrl_ops rotate_ctrl_ops = { 610 + .s_ctrl = rotate_s_ctrl, 611 + }; 612 + 613 + static int rotate_setup_ctrls(struct rotate_ctx *ctx) 614 + { 615 + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); 616 + 617 + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, 618 + V4L2_CID_HFLIP, 0, 1, 1, 0); 619 + 620 + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, 621 + V4L2_CID_VFLIP, 0, 1, 1, 0); 622 + 623 + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, 624 + V4L2_CID_ROTATE, 0, 270, 90, 0); 625 + 626 + if (ctx->ctrl_handler.error) { 627 + int err = ctx->ctrl_handler.error; 628 + 629 + v4l2_err(&ctx->dev->v4l2_dev, "control setup failed!\n"); 630 + v4l2_ctrl_handler_free(&ctx->ctrl_handler); 631 + 632 + return err; 633 + } 634 + 635 + return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); 636 + } 637 + 638 + static int rotate_open(struct file *file) 639 + { 640 + struct rotate_dev *dev = video_drvdata(file); 641 + struct rotate_ctx *ctx = NULL; 642 + int ret; 643 + 644 + if (mutex_lock_interruptible(&dev->dev_mutex)) 645 + return -ERESTARTSYS; 646 + 647 + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 648 + if (!ctx) { 649 + mutex_unlock(&dev->dev_mutex); 650 + return -ENOMEM; 651 + } 652 + 653 + /* default output format */ 654 + ctx->src_fmt.pixelformat = V4L2_PIX_FMT_ARGB32; 655 + ctx->src_fmt.field = V4L2_FIELD_NONE; 656 + ctx->src_fmt.width = 640; 657 + ctx->src_fmt.height = 480; 658 + rotate_prepare_format(&ctx->src_fmt); 659 + 660 + /* default capture format */ 661 + rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate); 662 + 663 + v4l2_fh_init(&ctx->fh, video_devdata(file)); 664 + file->private_data = &ctx->fh; 665 + ctx->dev = dev; 666 + 667 + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 668 + &rotate_queue_init); 669 + if (IS_ERR(ctx->fh.m2m_ctx)) { 670 + ret = PTR_ERR(ctx->fh.m2m_ctx); 671 + goto err_free; 672 + } 673 + 674 + v4l2_fh_add(&ctx->fh); 675 + 676 + ret = rotate_setup_ctrls(ctx); 677 + if (ret) 678 + goto err_free; 679 + 680 + ctx->fh.ctrl_handler = &ctx->ctrl_handler; 681 + 682 + mutex_unlock(&dev->dev_mutex); 683 + 684 + return 0; 685 + 686 + err_free: 687 + kfree(ctx); 688 + mutex_unlock(&dev->dev_mutex); 689 + 690 + return ret; 691 + } 692 + 693 + static int rotate_release(struct file *file) 694 + { 695 + struct rotate_dev *dev = video_drvdata(file); 696 + struct rotate_ctx *ctx = container_of(file->private_data, 697 + struct rotate_ctx, fh); 698 + 699 + mutex_lock(&dev->dev_mutex); 700 + 701 + v4l2_ctrl_handler_free(&ctx->ctrl_handler); 702 + v4l2_fh_del(&ctx->fh); 703 + v4l2_fh_exit(&ctx->fh); 704 + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 705 + 706 + kfree(ctx); 707 + 708 + mutex_unlock(&dev->dev_mutex); 709 + 710 + return 0; 711 + } 712 + 713 + static const struct v4l2_file_operations rotate_fops = { 714 + .owner = THIS_MODULE, 715 + .open = rotate_open, 716 + .release = rotate_release, 717 + .poll = v4l2_m2m_fop_poll, 718 + .unlocked_ioctl = video_ioctl2, 719 + .mmap = v4l2_m2m_fop_mmap, 720 + }; 721 + 722 + static const struct video_device rotate_video_device = { 723 + .name = ROTATE_NAME, 724 + .vfl_dir = VFL_DIR_M2M, 725 + .fops = &rotate_fops, 726 + .ioctl_ops = &rotate_ioctl_ops, 727 + .minor = -1, 728 + .release = video_device_release_empty, 729 + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, 730 + }; 731 + 732 + static const struct v4l2_m2m_ops rotate_m2m_ops = { 733 + .device_run = rotate_device_run, 734 + }; 735 + 736 + static int rotate_probe(struct platform_device *pdev) 737 + { 738 + struct rotate_dev *dev; 739 + struct video_device *vfd; 740 + int irq, ret; 741 + 742 + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 743 + if (!dev) 744 + return -ENOMEM; 745 + 746 + dev->vfd = rotate_video_device; 747 + dev->dev = &pdev->dev; 748 + 749 + irq = platform_get_irq(pdev, 0); 750 + if (irq <= 0) { 751 + dev_err(dev->dev, "Failed to get IRQ\n"); 752 + 753 + return irq; 754 + } 755 + 756 + ret = devm_request_irq(dev->dev, irq, rotate_irq, 757 + 0, dev_name(dev->dev), dev); 758 + if (ret) { 759 + dev_err(dev->dev, "Failed to request IRQ\n"); 760 + 761 + return ret; 762 + } 763 + 764 + dev->base = devm_platform_ioremap_resource(pdev, 0); 765 + if (IS_ERR(dev->base)) 766 + return PTR_ERR(dev->base); 767 + 768 + dev->bus_clk = devm_clk_get(dev->dev, "bus"); 769 + if (IS_ERR(dev->bus_clk)) { 770 + dev_err(dev->dev, "Failed to get bus clock\n"); 771 + 772 + return PTR_ERR(dev->bus_clk); 773 + } 774 + 775 + dev->mod_clk = devm_clk_get(dev->dev, "mod"); 776 + if (IS_ERR(dev->mod_clk)) { 777 + dev_err(dev->dev, "Failed to get mod clock\n"); 778 + 779 + return PTR_ERR(dev->mod_clk); 780 + } 781 + 782 + dev->rstc = devm_reset_control_get(dev->dev, NULL); 783 + if (IS_ERR(dev->rstc)) { 784 + dev_err(dev->dev, "Failed to get reset control\n"); 785 + 786 + return PTR_ERR(dev->rstc); 787 + } 788 + 789 + mutex_init(&dev->dev_mutex); 790 + 791 + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 792 + if (ret) { 793 + dev_err(dev->dev, "Failed to register V4L2 device\n"); 794 + 795 + return ret; 796 + } 797 + 798 + vfd = &dev->vfd; 799 + vfd->lock = &dev->dev_mutex; 800 + vfd->v4l2_dev = &dev->v4l2_dev; 801 + 802 + snprintf(vfd->name, sizeof(vfd->name), "%s", 803 + rotate_video_device.name); 804 + video_set_drvdata(vfd, dev); 805 + 806 + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 807 + if (ret) { 808 + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 809 + 810 + goto err_v4l2; 811 + } 812 + 813 + v4l2_info(&dev->v4l2_dev, 814 + "Device registered as /dev/video%d\n", vfd->num); 815 + 816 + dev->m2m_dev = v4l2_m2m_init(&rotate_m2m_ops); 817 + if (IS_ERR(dev->m2m_dev)) { 818 + v4l2_err(&dev->v4l2_dev, 819 + "Failed to initialize V4L2 M2M device\n"); 820 + ret = PTR_ERR(dev->m2m_dev); 821 + 822 + goto err_video; 823 + } 824 + 825 + platform_set_drvdata(pdev, dev); 826 + 827 + pm_runtime_enable(dev->dev); 828 + 829 + return 0; 830 + 831 + err_video: 832 + video_unregister_device(&dev->vfd); 833 + err_v4l2: 834 + v4l2_device_unregister(&dev->v4l2_dev); 835 + 836 + return ret; 837 + } 838 + 839 + static int rotate_remove(struct platform_device *pdev) 840 + { 841 + struct rotate_dev *dev = platform_get_drvdata(pdev); 842 + 843 + v4l2_m2m_release(dev->m2m_dev); 844 + video_unregister_device(&dev->vfd); 845 + v4l2_device_unregister(&dev->v4l2_dev); 846 + 847 + pm_runtime_force_suspend(&pdev->dev); 848 + 849 + return 0; 850 + } 851 + 852 + static int rotate_runtime_resume(struct device *device) 853 + { 854 + struct rotate_dev *dev = dev_get_drvdata(device); 855 + int ret; 856 + 857 + ret = clk_prepare_enable(dev->bus_clk); 858 + if (ret) { 859 + dev_err(dev->dev, "Failed to enable bus clock\n"); 860 + 861 + return ret; 862 + } 863 + 864 + ret = clk_prepare_enable(dev->mod_clk); 865 + if (ret) { 866 + dev_err(dev->dev, "Failed to enable mod clock\n"); 867 + 868 + goto err_bus_clk; 869 + } 870 + 871 + ret = reset_control_deassert(dev->rstc); 872 + if (ret) { 873 + dev_err(dev->dev, "Failed to apply reset\n"); 874 + 875 + goto err_mod_clk; 876 + } 877 + 878 + return 0; 879 + 880 + err_mod_clk: 881 + clk_disable_unprepare(dev->mod_clk); 882 + err_bus_clk: 883 + clk_disable_unprepare(dev->bus_clk); 884 + 885 + return ret; 886 + } 887 + 888 + static int rotate_runtime_suspend(struct device *device) 889 + { 890 + struct rotate_dev *dev = dev_get_drvdata(device); 891 + 892 + reset_control_assert(dev->rstc); 893 + 894 + clk_disable_unprepare(dev->mod_clk); 895 + clk_disable_unprepare(dev->bus_clk); 896 + 897 + return 0; 898 + } 899 + 900 + static const struct of_device_id rotate_dt_match[] = { 901 + { .compatible = "allwinner,sun8i-a83t-de2-rotate" }, 902 + { /* sentinel */ } 903 + }; 904 + MODULE_DEVICE_TABLE(of, rotate_dt_match); 905 + 906 + static const struct dev_pm_ops rotate_pm_ops = { 907 + .runtime_resume = rotate_runtime_resume, 908 + .runtime_suspend = rotate_runtime_suspend, 909 + }; 910 + 911 + static struct platform_driver rotate_driver = { 912 + .probe = rotate_probe, 913 + .remove = rotate_remove, 914 + .driver = { 915 + .name = ROTATE_NAME, 916 + .of_match_table = rotate_dt_match, 917 + .pm = &rotate_pm_ops, 918 + }, 919 + }; 920 + module_platform_driver(rotate_driver); 921 + 922 + MODULE_LICENSE("GPL v2"); 923 + MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>"); 924 + MODULE_DESCRIPTION("Allwinner DE2 rotate driver");