diff options
author | Liu Ying <Ying.Liu@freescale.com> | 2013-08-05 03:47:22 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:01:04 -0400 |
commit | 57069a5dd4f48dbf88caf9c959a4353db5301032 (patch) | |
tree | 268447a1bb3ad843a43bc88d72084057b4fde8fa | |
parent | 7cf40c09dd83a769823e1339a83148fe8efdcb25 (diff) |
ENGR00273974-1 media: port mxc vout driver from 3.5.7 to 3.10
This is porting mxc vout driver from imx_3.5.7 kernel to
imx_3.10 kernel.
* Put the driver in drivers/media/platform/ directory instead
of drivers/media/video/ directory, since the later one is
renamed to the former one in 3.10 kernel.
* Change the ipu-v3.h header file from <mach/ipu-v3.h> to
<linux/ipu-v3.h>.
* Change the mxc_vidioc_s_crop() function's implementation since
the definition of the vidioc_s_crop() function is modified to
make the last argument be constant.
* Set vfl_dir to be VFL_DIR_TX since the ioctrl validity checks
are improved in 3.10 kernel.
* Remove 'defaut y' setting for VIDEO_MXC_OUTPUT and
VIDEO_MXC_IPU_OUTPUT Kconfigs. They may be configured by kernel
default configure or by user.
* Make VIDEO_MXC_OUTPUT Kconfig depend on FB_MXC Kconfig since
we need framebuffers to be rendered.
* Make VIDEO_MXC_IPU_OUTPUT Kconfig be tristate.
* Split <linux/mxc_v4l2.h> header file up into include/linux/
and include/uapi/linux/ directories so that the userspace may
include the mxc_v4l2.h header file.
Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
-rw-r--r-- | drivers/media/platform/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/platform/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/platform/mxc/output/Kconfig | 5 | ||||
-rw-r--r-- | drivers/media/platform/mxc/output/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/mxc/output/mxc_vout.c | 2189 | ||||
-rw-r--r-- | include/linux/mxc_v4l2.h | 27 | ||||
-rw-r--r-- | include/uapi/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/uapi/linux/mxc_v4l2.h | 56 |
8 files changed, 2289 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 0494d2769fd7..1698a4e0cf74 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig | |||
@@ -121,6 +121,14 @@ config VIDEO_S3C_CAMIF | |||
121 | To compile this driver as a module, choose M here: the module | 121 | To compile this driver as a module, choose M here: the module |
122 | will be called s3c-camif. | 122 | will be called s3c-camif. |
123 | 123 | ||
124 | config VIDEO_MXC_OUTPUT | ||
125 | tristate "MXC Video For Linux Video Output" | ||
126 | depends on VIDEO_DEV && ARCH_MXC && FB_MXC | ||
127 | select VIDEOBUF_DMA_CONTIG | ||
128 | ---help--- | ||
129 | This is the video4linux2 output driver based on MXC module. | ||
130 | |||
131 | source "drivers/media/platform/mxc/output/Kconfig" | ||
124 | source "drivers/media/platform/soc_camera/Kconfig" | 132 | source "drivers/media/platform/soc_camera/Kconfig" |
125 | source "drivers/media/platform/exynos4-is/Kconfig" | 133 | source "drivers/media/platform/exynos4-is/Kconfig" |
126 | source "drivers/media/platform/s5p-tv/Kconfig" | 134 | source "drivers/media/platform/s5p-tv/Kconfig" |
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index eee28dd78d7d..43b85c3828cc 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile | |||
@@ -50,4 +50,6 @@ obj-y += davinci/ | |||
50 | 50 | ||
51 | obj-$(CONFIG_ARCH_OMAP) += omap/ | 51 | obj-$(CONFIG_ARCH_OMAP) += omap/ |
52 | 52 | ||
53 | obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/ | ||
54 | |||
53 | ccflags-y += -I$(srctree)/drivers/media/i2c | 55 | ccflags-y += -I$(srctree)/drivers/media/i2c |
diff --git a/drivers/media/platform/mxc/output/Kconfig b/drivers/media/platform/mxc/output/Kconfig new file mode 100644 index 000000000000..b684060abc18 --- /dev/null +++ b/drivers/media/platform/mxc/output/Kconfig | |||
@@ -0,0 +1,5 @@ | |||
1 | config VIDEO_MXC_IPU_OUTPUT | ||
2 | tristate "IPU v4l2 output support" | ||
3 | depends on VIDEO_MXC_OUTPUT && MXC_IPU | ||
4 | ---help--- | ||
5 | This is the video4linux2 driver for IPU post processing video output. | ||
diff --git a/drivers/media/platform/mxc/output/Makefile b/drivers/media/platform/mxc/output/Makefile new file mode 100644 index 000000000000..7b524fe8e616 --- /dev/null +++ b/drivers/media/platform/mxc/output/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc_vout.o | |||
diff --git a/drivers/media/platform/mxc/output/mxc_vout.c b/drivers/media/platform/mxc/output/mxc_vout.c new file mode 100644 index 000000000000..0dfe387aab2c --- /dev/null +++ b/drivers/media/platform/mxc/output/mxc_vout.c | |||
@@ -0,0 +1,2189 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * The code contained herein is licensed under the GNU General Public | ||
7 | * License. You may obtain a copy of the GNU General Public License | ||
8 | * Version 2 or later at the following locations: | ||
9 | * | ||
10 | * http://www.opensource.org/licenses/gpl-license.html | ||
11 | * http://www.gnu.org/copyleft/gpl.html | ||
12 | */ | ||
13 | |||
14 | #include <linux/console.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/ipu-v3.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mxcfb.h> | ||
20 | #include <linux/mxc_v4l2.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/videodev2.h> | ||
25 | #include <linux/vmalloc.h> | ||
26 | |||
27 | #include <media/videobuf-dma-contig.h> | ||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-ioctl.h> | ||
30 | |||
31 | #define UYVY_BLACK (0x00800080) | ||
32 | #define RGB_BLACK (0x0) | ||
33 | #define UV_BLACK (0x80) | ||
34 | #define Y_BLACK (0x0) | ||
35 | |||
36 | #define MAX_FB_NUM 6 | ||
37 | #define FB_BUFS 3 | ||
38 | #define VDOA_FB_BUFS (FB_BUFS - 1) | ||
39 | #define VALID_HEIGHT_1080P (1080) | ||
40 | #define FRAME_HEIGHT_1080P (1088) | ||
41 | #define FRAME_WIDTH_1080P (1920) | ||
42 | #define CHECK_TILED_1080P_DISPLAY(vout) \ | ||
43 | ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ | ||
44 | ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ | ||
45 | ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ | ||
46 | ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ | ||
47 | ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ | ||
48 | (((vout)->task.input.crop.h == FRAME_HEIGHT_1080P) || \ | ||
49 | ((vout)->task.input.crop.h == VALID_HEIGHT_1080P)) && \ | ||
50 | ((vout)->task.output.width == FRAME_WIDTH_1080P) && \ | ||
51 | ((vout)->task.output.height == VALID_HEIGHT_1080P) && \ | ||
52 | ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \ | ||
53 | ((vout)->task.output.crop.h == VALID_HEIGHT_1080P)) | ||
54 | #define CHECK_TILED_1080P_STREAM(vout) \ | ||
55 | ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ | ||
56 | ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ | ||
57 | ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ | ||
58 | ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ | ||
59 | ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ | ||
60 | ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) | ||
61 | #define IS_PLANAR_PIXEL_FORMAT(format) \ | ||
62 | (format == IPU_PIX_FMT_NV12 || \ | ||
63 | format == IPU_PIX_FMT_YUV420P2 || \ | ||
64 | format == IPU_PIX_FMT_YUV420P || \ | ||
65 | format == IPU_PIX_FMT_YVU420P || \ | ||
66 | format == IPU_PIX_FMT_YUV422P || \ | ||
67 | format == IPU_PIX_FMT_YVU422P || \ | ||
68 | format == IPU_PIX_FMT_YUV444P) | ||
69 | |||
70 | #define NSEC_PER_FRAME_30FPS (33333333) | ||
71 | |||
72 | struct mxc_vout_fb { | ||
73 | char *name; | ||
74 | int ipu_id; | ||
75 | struct v4l2_rect crop_bounds; | ||
76 | unsigned int disp_fmt; | ||
77 | bool disp_support_csc; | ||
78 | bool disp_support_windows; | ||
79 | }; | ||
80 | |||
81 | struct dma_mem { | ||
82 | void *vaddr; | ||
83 | dma_addr_t paddr; | ||
84 | size_t size; | ||
85 | }; | ||
86 | |||
87 | struct mxc_vout_output { | ||
88 | int open_cnt; | ||
89 | struct fb_info *fbi; | ||
90 | unsigned long fb_smem_start; | ||
91 | unsigned long fb_smem_len; | ||
92 | struct video_device *vfd; | ||
93 | struct mutex mutex; | ||
94 | struct mutex task_lock; | ||
95 | enum v4l2_buf_type type; | ||
96 | |||
97 | struct videobuf_queue vbq; | ||
98 | spinlock_t vbq_lock; | ||
99 | |||
100 | struct list_head queue_list; | ||
101 | struct list_head active_list; | ||
102 | |||
103 | struct v4l2_rect crop_bounds; | ||
104 | unsigned int disp_fmt; | ||
105 | struct mxcfb_pos win_pos; | ||
106 | bool disp_support_windows; | ||
107 | bool disp_support_csc; | ||
108 | |||
109 | bool fmt_init; | ||
110 | bool release; | ||
111 | bool linear_bypass_pp; | ||
112 | bool vdoa_1080p; | ||
113 | bool tiled_bypass_pp; | ||
114 | struct v4l2_rect in_rect; | ||
115 | struct ipu_task task; | ||
116 | struct ipu_task vdoa_task; | ||
117 | struct dma_mem vdoa_work; | ||
118 | struct dma_mem vdoa_output[VDOA_FB_BUFS]; | ||
119 | |||
120 | bool timer_stop; | ||
121 | struct hrtimer timer; | ||
122 | struct workqueue_struct *v4l_wq; | ||
123 | struct work_struct disp_work; | ||
124 | unsigned long frame_count; | ||
125 | unsigned long vdi_frame_cnt; | ||
126 | ktime_t start_ktime; | ||
127 | |||
128 | int ctrl_rotate; | ||
129 | int ctrl_vflip; | ||
130 | int ctrl_hflip; | ||
131 | |||
132 | dma_addr_t disp_bufs[FB_BUFS]; | ||
133 | |||
134 | struct videobuf_buffer *pre1_vb; | ||
135 | struct videobuf_buffer *pre2_vb; | ||
136 | }; | ||
137 | |||
138 | struct mxc_vout_dev { | ||
139 | struct device *dev; | ||
140 | struct v4l2_device v4l2_dev; | ||
141 | struct mxc_vout_output *out[MAX_FB_NUM]; | ||
142 | int out_num; | ||
143 | }; | ||
144 | |||
145 | /* Driver Configuration macros */ | ||
146 | #define VOUT_NAME "mxc_vout" | ||
147 | |||
148 | /* Variables configurable through module params*/ | ||
149 | static int debug; | ||
150 | static int vdi_rate_double; | ||
151 | static int video_nr = 16; | ||
152 | |||
153 | /* Module parameters */ | ||
154 | module_param(video_nr, int, S_IRUGO); | ||
155 | MODULE_PARM_DESC(video_nr, "video device numbers"); | ||
156 | module_param(debug, int, 0600); | ||
157 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); | ||
158 | module_param(vdi_rate_double, int, 0600); | ||
159 | MODULE_PARM_DESC(vdi_rate_double, "vdi frame rate double on/off"); | ||
160 | |||
161 | static const struct v4l2_fmtdesc mxc_formats[] = { | ||
162 | { | ||
163 | .description = "RGB565", | ||
164 | .pixelformat = V4L2_PIX_FMT_RGB565, | ||
165 | }, | ||
166 | { | ||
167 | .description = "BGR24", | ||
168 | .pixelformat = V4L2_PIX_FMT_BGR24, | ||
169 | }, | ||
170 | { | ||
171 | .description = "RGB24", | ||
172 | .pixelformat = V4L2_PIX_FMT_RGB24, | ||
173 | }, | ||
174 | { | ||
175 | .description = "RGB32", | ||
176 | .pixelformat = V4L2_PIX_FMT_RGB32, | ||
177 | }, | ||
178 | { | ||
179 | .description = "BGR32", | ||
180 | .pixelformat = V4L2_PIX_FMT_BGR32, | ||
181 | }, | ||
182 | { | ||
183 | .description = "NV12", | ||
184 | .pixelformat = V4L2_PIX_FMT_NV12, | ||
185 | }, | ||
186 | { | ||
187 | .description = "UYVY", | ||
188 | .pixelformat = V4L2_PIX_FMT_UYVY, | ||
189 | }, | ||
190 | { | ||
191 | .description = "YUYV", | ||
192 | .pixelformat = V4L2_PIX_FMT_YUYV, | ||
193 | }, | ||
194 | { | ||
195 | .description = "YUV422 planar", | ||
196 | .pixelformat = V4L2_PIX_FMT_YUV422P, | ||
197 | }, | ||
198 | { | ||
199 | .description = "YUV444", | ||
200 | .pixelformat = V4L2_PIX_FMT_YUV444, | ||
201 | }, | ||
202 | { | ||
203 | .description = "YUV420", | ||
204 | .pixelformat = V4L2_PIX_FMT_YUV420, | ||
205 | }, | ||
206 | { | ||
207 | .description = "YVU420", | ||
208 | .pixelformat = V4L2_PIX_FMT_YVU420, | ||
209 | }, | ||
210 | { | ||
211 | .description = "TILED NV12P", | ||
212 | .pixelformat = IPU_PIX_FMT_TILED_NV12, | ||
213 | }, | ||
214 | { | ||
215 | .description = "TILED NV12F", | ||
216 | .pixelformat = IPU_PIX_FMT_TILED_NV12F, | ||
217 | }, | ||
218 | { | ||
219 | .description = "YUV444 planar", | ||
220 | .pixelformat = IPU_PIX_FMT_YUV444P, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | #define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) | ||
225 | |||
226 | #define DEF_INPUT_WIDTH 320 | ||
227 | #define DEF_INPUT_HEIGHT 240 | ||
228 | |||
229 | static int mxc_vidioc_streamoff(struct file *file, void *fh, | ||
230 | enum v4l2_buf_type i); | ||
231 | |||
232 | static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; | ||
233 | static int config_disp_output(struct mxc_vout_output *vout); | ||
234 | static void release_disp_output(struct mxc_vout_output *vout); | ||
235 | |||
236 | static unsigned int get_frame_size(struct mxc_vout_output *vout) | ||
237 | { | ||
238 | unsigned int size; | ||
239 | |||
240 | if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) | ||
241 | size = TILED_NV12_FRAME_SIZE(vout->task.input.width, | ||
242 | vout->task.input.height); | ||
243 | else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { | ||
244 | size = TILED_NV12_FRAME_SIZE(vout->task.input.width, | ||
245 | vout->task.input.height/2); | ||
246 | size *= 2; | ||
247 | } else | ||
248 | size = vout->task.input.width * vout->task.input.height * | ||
249 | fmt_to_bpp(vout->task.input.format)/8; | ||
250 | |||
251 | return size; | ||
252 | } | ||
253 | |||
254 | static void free_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) | ||
255 | { | ||
256 | dma_free_coherent(vout->vbq.dev, buf->size, buf->vaddr, buf->paddr); | ||
257 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
258 | "free dma size:0x%x, paddr:0x%x\n", | ||
259 | buf->size, buf->paddr); | ||
260 | memset(buf, 0, sizeof(*buf)); | ||
261 | } | ||
262 | |||
263 | static int alloc_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) | ||
264 | { | ||
265 | |||
266 | buf->vaddr = dma_alloc_coherent(vout->vbq.dev, buf->size, &buf->paddr, | ||
267 | GFP_DMA | GFP_KERNEL); | ||
268 | if (!buf->vaddr) { | ||
269 | v4l2_err(vout->vfd->v4l2_dev, | ||
270 | "cannot get dma buf size:0x%x\n", buf->size); | ||
271 | return -ENOMEM; | ||
272 | } | ||
273 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
274 | "alloc dma buf size:0x%x, paddr:0x%x\n", buf->size, buf->paddr); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static ipu_channel_t get_ipu_channel(struct fb_info *fbi) | ||
279 | { | ||
280 | ipu_channel_t ipu_ch = CHAN_NONE; | ||
281 | mm_segment_t old_fs; | ||
282 | |||
283 | if (fbi->fbops->fb_ioctl) { | ||
284 | old_fs = get_fs(); | ||
285 | set_fs(KERNEL_DS); | ||
286 | fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, | ||
287 | (unsigned long)&ipu_ch); | ||
288 | set_fs(old_fs); | ||
289 | } | ||
290 | |||
291 | return ipu_ch; | ||
292 | } | ||
293 | |||
294 | static unsigned int get_ipu_fmt(struct fb_info *fbi) | ||
295 | { | ||
296 | mm_segment_t old_fs; | ||
297 | unsigned int fb_fmt; | ||
298 | |||
299 | if (fbi->fbops->fb_ioctl) { | ||
300 | old_fs = get_fs(); | ||
301 | set_fs(KERNEL_DS); | ||
302 | fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, | ||
303 | (unsigned long)&fb_fmt); | ||
304 | set_fs(old_fs); | ||
305 | } | ||
306 | |||
307 | return fb_fmt; | ||
308 | } | ||
309 | |||
310 | static void update_display_setting(void) | ||
311 | { | ||
312 | int i; | ||
313 | struct fb_info *fbi; | ||
314 | struct v4l2_rect bg_crop_bounds[2]; | ||
315 | |||
316 | for (i = 0; i < num_registered_fb; i++) { | ||
317 | fbi = registered_fb[i]; | ||
318 | |||
319 | memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb)); | ||
320 | |||
321 | if (!strncmp(fbi->fix.id, "DISP3", 5)) | ||
322 | g_fb_setting[i].ipu_id = 0; | ||
323 | else | ||
324 | g_fb_setting[i].ipu_id = 1; | ||
325 | |||
326 | g_fb_setting[i].name = fbi->fix.id; | ||
327 | g_fb_setting[i].crop_bounds.left = 0; | ||
328 | g_fb_setting[i].crop_bounds.top = 0; | ||
329 | g_fb_setting[i].crop_bounds.width = fbi->var.xres; | ||
330 | g_fb_setting[i].crop_bounds.height = fbi->var.yres; | ||
331 | g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi); | ||
332 | |||
333 | if (get_ipu_channel(fbi) == MEM_BG_SYNC) { | ||
334 | bg_crop_bounds[g_fb_setting[i].ipu_id] = | ||
335 | g_fb_setting[i].crop_bounds; | ||
336 | g_fb_setting[i].disp_support_csc = true; | ||
337 | } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) { | ||
338 | g_fb_setting[i].disp_support_csc = true; | ||
339 | g_fb_setting[i].disp_support_windows = true; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | for (i = 0; i < num_registered_fb; i++) { | ||
344 | fbi = registered_fb[i]; | ||
345 | |||
346 | if (get_ipu_channel(fbi) == MEM_FG_SYNC) | ||
347 | g_fb_setting[i].crop_bounds = | ||
348 | bg_crop_bounds[g_fb_setting[i].ipu_id]; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | /* called after g_fb_setting filled by update_display_setting */ | ||
353 | static int update_setting_from_fbi(struct mxc_vout_output *vout, | ||
354 | struct fb_info *fbi) | ||
355 | { | ||
356 | int i; | ||
357 | bool found = false; | ||
358 | |||
359 | for (i = 0; i < MAX_FB_NUM; i++) { | ||
360 | if (g_fb_setting[i].name) { | ||
361 | if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) { | ||
362 | vout->crop_bounds = g_fb_setting[i].crop_bounds; | ||
363 | vout->disp_fmt = g_fb_setting[i].disp_fmt; | ||
364 | vout->disp_support_csc = | ||
365 | g_fb_setting[i].disp_support_csc; | ||
366 | vout->disp_support_windows = | ||
367 | g_fb_setting[i].disp_support_windows; | ||
368 | found = true; | ||
369 | break; | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | if (!found) { | ||
375 | v4l2_err(vout->vfd->v4l2_dev, "can not find output\n"); | ||
376 | return -EINVAL; | ||
377 | } | ||
378 | strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); | ||
379 | |||
380 | memset(&vout->task, 0, sizeof(struct ipu_task)); | ||
381 | |||
382 | vout->task.input.width = DEF_INPUT_WIDTH; | ||
383 | vout->task.input.height = DEF_INPUT_HEIGHT; | ||
384 | vout->task.input.crop.pos.x = 0; | ||
385 | vout->task.input.crop.pos.y = 0; | ||
386 | vout->task.input.crop.w = DEF_INPUT_WIDTH; | ||
387 | vout->task.input.crop.h = DEF_INPUT_HEIGHT; | ||
388 | |||
389 | vout->task.output.width = vout->crop_bounds.width; | ||
390 | vout->task.output.height = vout->crop_bounds.height; | ||
391 | vout->task.output.crop.pos.x = 0; | ||
392 | vout->task.output.crop.pos.y = 0; | ||
393 | vout->task.output.crop.w = vout->crop_bounds.width; | ||
394 | vout->task.output.crop.h = vout->crop_bounds.height; | ||
395 | if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) | ||
396 | vout->task.output.format = IPU_PIX_FMT_UYVY; | ||
397 | else | ||
398 | vout->task.output.format = IPU_PIX_FMT_RGB565; | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static inline unsigned long get_jiffies(struct timeval *t) | ||
404 | { | ||
405 | struct timeval cur; | ||
406 | |||
407 | if (t->tv_usec >= 1000000) { | ||
408 | t->tv_sec += t->tv_usec / 1000000; | ||
409 | t->tv_usec = t->tv_usec % 1000000; | ||
410 | } | ||
411 | |||
412 | do_gettimeofday(&cur); | ||
413 | if ((t->tv_sec < cur.tv_sec) | ||
414 | || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) | ||
415 | return jiffies; | ||
416 | |||
417 | if (t->tv_usec < cur.tv_usec) { | ||
418 | cur.tv_sec = t->tv_sec - cur.tv_sec - 1; | ||
419 | cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; | ||
420 | } else { | ||
421 | cur.tv_sec = t->tv_sec - cur.tv_sec; | ||
422 | cur.tv_usec = t->tv_usec - cur.tv_usec; | ||
423 | } | ||
424 | |||
425 | return jiffies + timeval_to_jiffies(&cur); | ||
426 | } | ||
427 | |||
428 | static bool deinterlace_3_field(struct mxc_vout_output *vout) | ||
429 | { | ||
430 | return (vout->task.input.deinterlace.enable && | ||
431 | (vout->task.input.deinterlace.motion != HIGH_MOTION)); | ||
432 | } | ||
433 | |||
434 | static int set_field_fmt(struct mxc_vout_output *vout, enum v4l2_field field) | ||
435 | { | ||
436 | struct ipu_deinterlace *deinterlace = &vout->task.input.deinterlace; | ||
437 | |||
438 | switch (field) { | ||
439 | /* Images are in progressive format, not interlaced */ | ||
440 | case V4L2_FIELD_NONE: | ||
441 | case V4L2_FIELD_ANY: | ||
442 | deinterlace->enable = false; | ||
443 | deinterlace->field_fmt = 0; | ||
444 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "Progressive frame.\n"); | ||
445 | break; | ||
446 | case V4L2_FIELD_INTERLACED_TB: | ||
447 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
448 | "Enable deinterlace TB.\n"); | ||
449 | deinterlace->enable = true; | ||
450 | deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_TOP; | ||
451 | break; | ||
452 | case V4L2_FIELD_INTERLACED_BT: | ||
453 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
454 | "Enable deinterlace BT.\n"); | ||
455 | deinterlace->enable = true; | ||
456 | deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_BOTTOM; | ||
457 | break; | ||
458 | default: | ||
459 | v4l2_err(vout->vfd->v4l2_dev, | ||
460 | "field format:%d not supported yet!\n", field); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { | ||
465 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
466 | "tiled fmt enable deinterlace.\n"); | ||
467 | deinterlace->enable = true; | ||
468 | } | ||
469 | |||
470 | if (deinterlace->enable && vdi_rate_double) | ||
471 | deinterlace->field_fmt |= IPU_DEINTERLACE_RATE_EN; | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static bool is_pp_bypass(struct mxc_vout_output *vout) | ||
477 | { | ||
478 | if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || | ||
479 | (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format)) | ||
480 | return false; | ||
481 | if ((vout->task.input.width == vout->task.output.width) && | ||
482 | (vout->task.input.height == vout->task.output.height) && | ||
483 | (vout->task.input.crop.w == vout->task.output.crop.w) && | ||
484 | (vout->task.input.crop.h == vout->task.output.crop.h) && | ||
485 | (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && | ||
486 | !vout->task.input.deinterlace.enable) { | ||
487 | if (vout->disp_support_csc) | ||
488 | return true; | ||
489 | else if (!need_csc(vout->task.input.format, vout->disp_fmt)) | ||
490 | return true; | ||
491 | /* | ||
492 | * input crop show to full output which can show based on | ||
493 | * xres_virtual/yres_virtual | ||
494 | */ | ||
495 | } else if ((vout->task.input.crop.w == vout->task.output.crop.w) && | ||
496 | (vout->task.output.crop.w == vout->task.output.width) && | ||
497 | (vout->task.input.crop.h == vout->task.output.crop.h) && | ||
498 | (vout->task.output.crop.h == | ||
499 | vout->task.output.height) && | ||
500 | (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && | ||
501 | !vout->task.input.deinterlace.enable) { | ||
502 | if (vout->disp_support_csc) | ||
503 | return true; | ||
504 | else if (!need_csc(vout->task.input.format, vout->disp_fmt)) | ||
505 | return true; | ||
506 | } | ||
507 | return false; | ||
508 | } | ||
509 | |||
510 | static void setup_buf_timer(struct mxc_vout_output *vout, | ||
511 | struct videobuf_buffer *vb) | ||
512 | { | ||
513 | ktime_t expiry_time, now; | ||
514 | |||
515 | /* if timestamp is 0, then default to 30fps */ | ||
516 | if ((vb->ts.tv_sec == 0) && (vb->ts.tv_usec == 0)) | ||
517 | expiry_time = ktime_add_ns(vout->start_ktime, | ||
518 | NSEC_PER_FRAME_30FPS * vout->frame_count); | ||
519 | else | ||
520 | expiry_time = timeval_to_ktime(vb->ts); | ||
521 | |||
522 | now = hrtimer_cb_get_time(&vout->timer); | ||
523 | if ((now.tv64 > expiry_time.tv64)) { | ||
524 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
525 | "warning: timer timeout already expired.\n"); | ||
526 | expiry_time = now; | ||
527 | } | ||
528 | |||
529 | hrtimer_start(&vout->timer, expiry_time, HRTIMER_MODE_ABS); | ||
530 | |||
531 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "timer handler next " | ||
532 | "schedule: %lldnsecs\n", expiry_time.tv64); | ||
533 | } | ||
534 | |||
535 | static int show_buf(struct mxc_vout_output *vout, int idx, | ||
536 | struct ipu_pos *ipos) | ||
537 | { | ||
538 | struct fb_info *fbi = vout->fbi; | ||
539 | struct fb_var_screeninfo var; | ||
540 | int ret; | ||
541 | u32 fb_base = 0; | ||
542 | |||
543 | memcpy(&var, &fbi->var, sizeof(var)); | ||
544 | |||
545 | if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { | ||
546 | /* | ||
547 | * crack fb base | ||
548 | * NOTE: should not do other fb operation during v4l2 | ||
549 | */ | ||
550 | console_lock(); | ||
551 | fb_base = fbi->fix.smem_start; | ||
552 | fbi->fix.smem_start = vout->task.output.paddr; | ||
553 | fbi->var.yoffset = ipos->y + 1; | ||
554 | var.xoffset = ipos->x; | ||
555 | var.yoffset = ipos->y; | ||
556 | var.vmode |= FB_VMODE_YWRAP; | ||
557 | ret = fb_pan_display(fbi, &var); | ||
558 | fbi->fix.smem_start = fb_base; | ||
559 | console_unlock(); | ||
560 | } else { | ||
561 | console_lock(); | ||
562 | var.yoffset = idx * fbi->var.yres; | ||
563 | var.vmode &= ~FB_VMODE_YWRAP; | ||
564 | ret = fb_pan_display(fbi, &var); | ||
565 | console_unlock(); | ||
566 | } | ||
567 | |||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | static void disp_work_func(struct work_struct *work) | ||
572 | { | ||
573 | struct mxc_vout_output *vout = | ||
574 | container_of(work, struct mxc_vout_output, disp_work); | ||
575 | struct videobuf_queue *q = &vout->vbq; | ||
576 | struct videobuf_buffer *vb, *vb_next = NULL; | ||
577 | unsigned long flags = 0; | ||
578 | struct ipu_pos ipos; | ||
579 | int ret = 0; | ||
580 | u32 in_fmt = 0; | ||
581 | u32 vdi_cnt = 0; | ||
582 | u32 vdi_frame; | ||
583 | u32 index = 0; | ||
584 | u32 ocrop_h = 0; | ||
585 | u32 o_height = 0; | ||
586 | u32 tiled_interlaced = 0; | ||
587 | bool tiled_fmt = false; | ||
588 | |||
589 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); | ||
590 | |||
591 | spin_lock_irqsave(q->irqlock, flags); | ||
592 | |||
593 | if (list_empty(&vout->active_list)) { | ||
594 | v4l2_warn(vout->vfd->v4l2_dev, | ||
595 | "no entry in active_list, should not be here\n"); | ||
596 | spin_unlock_irqrestore(q->irqlock, flags); | ||
597 | return; | ||
598 | } | ||
599 | |||
600 | vb = list_first_entry(&vout->active_list, | ||
601 | struct videobuf_buffer, queue); | ||
602 | ret = set_field_fmt(vout, vb->field); | ||
603 | if (ret < 0) { | ||
604 | spin_unlock_irqrestore(q->irqlock, flags); | ||
605 | return; | ||
606 | } | ||
607 | if (deinterlace_3_field(vout)) { | ||
608 | if (list_is_singular(&vout->active_list)) { | ||
609 | if (list_empty(&vout->queue_list)) { | ||
610 | vout->timer_stop = true; | ||
611 | spin_unlock_irqrestore(q->irqlock, flags); | ||
612 | v4l2_warn(vout->vfd->v4l2_dev, | ||
613 | "no enough entry for 3 fields " | ||
614 | "deinterlacer\n"); | ||
615 | return; | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * We need to use the next vb even if it is | ||
620 | * not on the active list. | ||
621 | */ | ||
622 | vb_next = list_first_entry(&vout->queue_list, | ||
623 | struct videobuf_buffer, queue); | ||
624 | } else | ||
625 | vb_next = list_first_entry(vout->active_list.next, | ||
626 | struct videobuf_buffer, queue); | ||
627 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
628 | "cur field_fmt:%d, next field_fmt:%d.\n", | ||
629 | vb->field, vb_next->field); | ||
630 | /* repeat the last field during field format changing */ | ||
631 | if ((vb->field != vb_next->field) && | ||
632 | (vb_next->field != V4L2_FIELD_NONE)) | ||
633 | vb_next = vb; | ||
634 | } | ||
635 | |||
636 | spin_unlock_irqrestore(q->irqlock, flags); | ||
637 | |||
638 | vdi_frame_rate_double: | ||
639 | mutex_lock(&vout->task_lock); | ||
640 | |||
641 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
642 | "v4l2 frame_cnt:%ld, vb_field:%d, fmt:%d\n", | ||
643 | vout->frame_count, vb->field, | ||
644 | vout->task.input.deinterlace.field_fmt); | ||
645 | if (vb->memory == V4L2_MEMORY_USERPTR) | ||
646 | vout->task.input.paddr = vb->baddr; | ||
647 | else | ||
648 | vout->task.input.paddr = videobuf_to_dma_contig(vb); | ||
649 | |||
650 | if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) | ||
651 | index = vout->vdi_frame_cnt % FB_BUFS; | ||
652 | else | ||
653 | index = vout->frame_count % FB_BUFS; | ||
654 | if (vout->linear_bypass_pp) { | ||
655 | vout->task.output.paddr = vout->task.input.paddr; | ||
656 | ipos.x = vout->task.input.crop.pos.x; | ||
657 | ipos.y = vout->task.input.crop.pos.y; | ||
658 | } else { | ||
659 | if (deinterlace_3_field(vout)) { | ||
660 | if (vb->memory == V4L2_MEMORY_USERPTR) | ||
661 | vout->task.input.paddr_n = vb_next->baddr; | ||
662 | else | ||
663 | vout->task.input.paddr_n = | ||
664 | videobuf_to_dma_contig(vb_next); | ||
665 | } | ||
666 | vout->task.output.paddr = vout->disp_bufs[index]; | ||
667 | if (vout->vdoa_1080p) { | ||
668 | o_height = vout->task.output.height; | ||
669 | ocrop_h = vout->task.output.crop.h; | ||
670 | vout->task.output.height = FRAME_HEIGHT_1080P; | ||
671 | vout->task.output.crop.h = FRAME_HEIGHT_1080P; | ||
672 | } | ||
673 | tiled_fmt = | ||
674 | (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || | ||
675 | (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format); | ||
676 | if (vout->tiled_bypass_pp) { | ||
677 | ipos.x = vout->task.input.crop.pos.x; | ||
678 | ipos.y = vout->task.input.crop.pos.y; | ||
679 | } else if (tiled_fmt) { | ||
680 | vout->vdoa_task.input.paddr = vout->task.input.paddr; | ||
681 | if (deinterlace_3_field(vout)) | ||
682 | vout->vdoa_task.input.paddr_n = | ||
683 | vout->task.input.paddr_n; | ||
684 | vout->vdoa_task.output.paddr = vout->vdoa_work.paddr; | ||
685 | ret = ipu_queue_task(&vout->vdoa_task); | ||
686 | if (ret < 0) { | ||
687 | mutex_unlock(&vout->task_lock); | ||
688 | goto err; | ||
689 | } | ||
690 | vout->task.input.paddr = vout->vdoa_task.output.paddr; | ||
691 | in_fmt = vout->task.input.format; | ||
692 | vout->task.input.format = vout->vdoa_task.output.format; | ||
693 | if (vout->task.input.deinterlace.enable) { | ||
694 | tiled_interlaced = 1; | ||
695 | vout->task.input.deinterlace.enable = 0; | ||
696 | } | ||
697 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
698 | "tiled queue task\n"); | ||
699 | } | ||
700 | ret = ipu_queue_task(&vout->task); | ||
701 | if ((!vout->tiled_bypass_pp) && tiled_fmt) | ||
702 | vout->task.input.format = in_fmt; | ||
703 | if (tiled_interlaced) | ||
704 | vout->task.input.deinterlace.enable = 1; | ||
705 | if (ret < 0) { | ||
706 | mutex_unlock(&vout->task_lock); | ||
707 | goto err; | ||
708 | } | ||
709 | if (vout->vdoa_1080p) { | ||
710 | vout->task.output.crop.h = ocrop_h; | ||
711 | vout->task.output.height = o_height; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | mutex_unlock(&vout->task_lock); | ||
716 | |||
717 | ret = show_buf(vout, index, &ipos); | ||
718 | if (ret < 0) | ||
719 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
720 | "show buf with ret %d\n", ret); | ||
721 | |||
722 | if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) { | ||
723 | vdi_frame = vout->task.input.deinterlace.field_fmt | ||
724 | & IPU_DEINTERLACE_RATE_FRAME1; | ||
725 | if (vdi_frame) | ||
726 | vout->task.input.deinterlace.field_fmt &= | ||
727 | ~IPU_DEINTERLACE_RATE_FRAME1; | ||
728 | else | ||
729 | vout->task.input.deinterlace.field_fmt |= | ||
730 | IPU_DEINTERLACE_RATE_FRAME1; | ||
731 | vout->vdi_frame_cnt++; | ||
732 | vdi_cnt++; | ||
733 | if (vdi_cnt < IPU_DEINTERLACE_MAX_FRAME) | ||
734 | goto vdi_frame_rate_double; | ||
735 | } | ||
736 | spin_lock_irqsave(q->irqlock, flags); | ||
737 | |||
738 | list_del(&vb->queue); | ||
739 | |||
740 | /* | ||
741 | * The videobuf before the last one has been shown. Set | ||
742 | * VIDEOBUF_DONE state here to avoid tearing issue in ic bypass | ||
743 | * case, which makes sure a buffer being shown will not be | ||
744 | * dequeued to be overwritten. It also brings side-effect that | ||
745 | * the last 2 buffers can not be dequeued correctly, apps need | ||
746 | * to take care of it. | ||
747 | */ | ||
748 | if (vout->pre2_vb) { | ||
749 | vout->pre2_vb->state = VIDEOBUF_DONE; | ||
750 | wake_up_interruptible(&vout->pre2_vb->done); | ||
751 | vout->pre2_vb = NULL; | ||
752 | } | ||
753 | |||
754 | if (vout->linear_bypass_pp) { | ||
755 | vout->pre2_vb = vout->pre1_vb; | ||
756 | vout->pre1_vb = vb; | ||
757 | } else { | ||
758 | if (vout->pre1_vb) { | ||
759 | vout->pre1_vb->state = VIDEOBUF_DONE; | ||
760 | wake_up_interruptible(&vout->pre1_vb->done); | ||
761 | vout->pre1_vb = NULL; | ||
762 | } | ||
763 | vb->state = VIDEOBUF_DONE; | ||
764 | wake_up_interruptible(&vb->done); | ||
765 | } | ||
766 | |||
767 | vout->frame_count++; | ||
768 | |||
769 | /* pick next queue buf to setup timer */ | ||
770 | if (list_empty(&vout->queue_list)) | ||
771 | vout->timer_stop = true; | ||
772 | else { | ||
773 | vb = list_first_entry(&vout->queue_list, | ||
774 | struct videobuf_buffer, queue); | ||
775 | setup_buf_timer(vout, vb); | ||
776 | } | ||
777 | |||
778 | spin_unlock_irqrestore(q->irqlock, flags); | ||
779 | |||
780 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n"); | ||
781 | |||
782 | return; | ||
783 | err: | ||
784 | v4l2_err(vout->vfd->v4l2_dev, "display work fail ret = %d\n", ret); | ||
785 | vout->timer_stop = true; | ||
786 | vb->state = VIDEOBUF_ERROR; | ||
787 | return; | ||
788 | } | ||
789 | |||
790 | static enum hrtimer_restart mxc_vout_timer_handler(struct hrtimer *timer) | ||
791 | { | ||
792 | struct mxc_vout_output *vout = container_of(timer, | ||
793 | struct mxc_vout_output, | ||
794 | timer); | ||
795 | struct videobuf_queue *q = &vout->vbq; | ||
796 | struct videobuf_buffer *vb; | ||
797 | unsigned long flags = 0; | ||
798 | |||
799 | spin_lock_irqsave(q->irqlock, flags); | ||
800 | |||
801 | /* | ||
802 | * put first queued entry into active, if previous entry did not | ||
803 | * finish, setup current entry's timer again. | ||
804 | */ | ||
805 | if (list_empty(&vout->queue_list)) { | ||
806 | spin_unlock_irqrestore(q->irqlock, flags); | ||
807 | return HRTIMER_NORESTART; | ||
808 | } | ||
809 | |||
810 | /* move videobuf from queued list to active list */ | ||
811 | vb = list_first_entry(&vout->queue_list, | ||
812 | struct videobuf_buffer, queue); | ||
813 | list_del(&vb->queue); | ||
814 | list_add_tail(&vb->queue, &vout->active_list); | ||
815 | |||
816 | if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) { | ||
817 | v4l2_warn(vout->vfd->v4l2_dev, | ||
818 | "disp work was in queue already, queue buf again next time\n"); | ||
819 | list_del(&vb->queue); | ||
820 | list_add(&vb->queue, &vout->queue_list); | ||
821 | spin_unlock_irqrestore(q->irqlock, flags); | ||
822 | return HRTIMER_NORESTART; | ||
823 | } | ||
824 | |||
825 | vb->state = VIDEOBUF_ACTIVE; | ||
826 | |||
827 | spin_unlock_irqrestore(q->irqlock, flags); | ||
828 | |||
829 | return HRTIMER_NORESTART; | ||
830 | } | ||
831 | |||
832 | /* Video buffer call backs */ | ||
833 | |||
834 | /* | ||
835 | * Buffer setup function is called by videobuf layer when REQBUF ioctl is | ||
836 | * called. This is used to setup buffers and return size and count of | ||
837 | * buffers allocated. After the call to this buffer, videobuf layer will | ||
838 | * setup buffer queue depending on the size and count of buffers | ||
839 | */ | ||
840 | static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, | ||
841 | unsigned int *size) | ||
842 | { | ||
843 | struct mxc_vout_output *vout = q->priv_data; | ||
844 | unsigned int frame_size; | ||
845 | |||
846 | if (!vout) | ||
847 | return -EINVAL; | ||
848 | |||
849 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) | ||
850 | return -EINVAL; | ||
851 | |||
852 | frame_size = get_frame_size(vout); | ||
853 | *size = PAGE_ALIGN(frame_size); | ||
854 | |||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * This function will be called when VIDIOC_QBUF ioctl is called. | ||
860 | * It prepare buffers before give out for the display. This function | ||
861 | * converts user space virtual address into physical address if userptr memory | ||
862 | * exchange mechanism is used. | ||
863 | */ | ||
864 | static int mxc_vout_buffer_prepare(struct videobuf_queue *q, | ||
865 | struct videobuf_buffer *vb, | ||
866 | enum v4l2_field field) | ||
867 | { | ||
868 | vb->state = VIDEOBUF_PREPARED; | ||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * Buffer queue funtion will be called from the videobuf layer when _QBUF | ||
874 | * ioctl is called. It is used to enqueue buffer, which is ready to be | ||
875 | * displayed. | ||
876 | * This function is protected by q->irqlock. | ||
877 | */ | ||
878 | static void mxc_vout_buffer_queue(struct videobuf_queue *q, | ||
879 | struct videobuf_buffer *vb) | ||
880 | { | ||
881 | struct mxc_vout_output *vout = q->priv_data; | ||
882 | struct videobuf_buffer *active_vb; | ||
883 | |||
884 | list_add_tail(&vb->queue, &vout->queue_list); | ||
885 | vb->state = VIDEOBUF_QUEUED; | ||
886 | |||
887 | if (vout->timer_stop) { | ||
888 | if (deinterlace_3_field(vout) && | ||
889 | !list_empty(&vout->active_list)) { | ||
890 | active_vb = list_first_entry(&vout->active_list, | ||
891 | struct videobuf_buffer, queue); | ||
892 | setup_buf_timer(vout, active_vb); | ||
893 | } else { | ||
894 | setup_buf_timer(vout, vb); | ||
895 | } | ||
896 | vout->timer_stop = false; | ||
897 | } | ||
898 | } | ||
899 | |||
900 | /* | ||
901 | * Buffer release function is called from videobuf layer to release buffer | ||
902 | * which are already allocated | ||
903 | */ | ||
904 | static void mxc_vout_buffer_release(struct videobuf_queue *q, | ||
905 | struct videobuf_buffer *vb) | ||
906 | { | ||
907 | vb->state = VIDEOBUF_NEEDS_INIT; | ||
908 | } | ||
909 | |||
910 | static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma) | ||
911 | { | ||
912 | int ret; | ||
913 | struct mxc_vout_output *vout = file->private_data; | ||
914 | |||
915 | if (!vout) | ||
916 | return -ENODEV; | ||
917 | |||
918 | ret = videobuf_mmap_mapper(&vout->vbq, vma); | ||
919 | if (ret < 0) | ||
920 | v4l2_err(vout->vfd->v4l2_dev, | ||
921 | "offset invalid [offset=0x%lx]\n", | ||
922 | (vma->vm_pgoff << PAGE_SHIFT)); | ||
923 | |||
924 | return ret; | ||
925 | } | ||
926 | |||
927 | static int mxc_vout_release(struct file *file) | ||
928 | { | ||
929 | unsigned int ret = 0; | ||
930 | struct videobuf_queue *q; | ||
931 | struct mxc_vout_output *vout = file->private_data; | ||
932 | |||
933 | if (!vout) | ||
934 | return 0; | ||
935 | |||
936 | if (--vout->open_cnt == 0) { | ||
937 | q = &vout->vbq; | ||
938 | if (q->streaming) | ||
939 | mxc_vidioc_streamoff(file, vout, vout->type); | ||
940 | else { | ||
941 | release_disp_output(vout); | ||
942 | videobuf_queue_cancel(q); | ||
943 | } | ||
944 | destroy_workqueue(vout->v4l_wq); | ||
945 | ret = videobuf_mmap_free(q); | ||
946 | } | ||
947 | |||
948 | return ret; | ||
949 | } | ||
950 | |||
951 | static int mxc_vout_open(struct file *file) | ||
952 | { | ||
953 | struct mxc_vout_output *vout = NULL; | ||
954 | int ret = 0; | ||
955 | |||
956 | vout = video_drvdata(file); | ||
957 | |||
958 | if (vout == NULL) | ||
959 | return -ENODEV; | ||
960 | |||
961 | if (vout->open_cnt++ == 0) { | ||
962 | vout->ctrl_rotate = 0; | ||
963 | vout->ctrl_vflip = 0; | ||
964 | vout->ctrl_hflip = 0; | ||
965 | update_display_setting(); | ||
966 | ret = update_setting_from_fbi(vout, vout->fbi); | ||
967 | if (ret < 0) | ||
968 | goto err; | ||
969 | |||
970 | vout->v4l_wq = create_singlethread_workqueue("v4l2q"); | ||
971 | if (!vout->v4l_wq) { | ||
972 | v4l2_err(vout->vfd->v4l2_dev, | ||
973 | "Could not create work queue\n"); | ||
974 | ret = -ENOMEM; | ||
975 | goto err; | ||
976 | } | ||
977 | |||
978 | INIT_WORK(&vout->disp_work, disp_work_func); | ||
979 | |||
980 | INIT_LIST_HEAD(&vout->queue_list); | ||
981 | INIT_LIST_HEAD(&vout->active_list); | ||
982 | |||
983 | vout->fmt_init = false; | ||
984 | vout->frame_count = 0; | ||
985 | vout->vdi_frame_cnt = 0; | ||
986 | |||
987 | vout->win_pos.x = 0; | ||
988 | vout->win_pos.y = 0; | ||
989 | vout->release = true; | ||
990 | } | ||
991 | |||
992 | file->private_data = vout; | ||
993 | |||
994 | err: | ||
995 | return ret; | ||
996 | } | ||
997 | |||
998 | /* | ||
999 | * V4L2 ioctls | ||
1000 | */ | ||
1001 | static int mxc_vidioc_querycap(struct file *file, void *fh, | ||
1002 | struct v4l2_capability *cap) | ||
1003 | { | ||
1004 | struct mxc_vout_output *vout = fh; | ||
1005 | |||
1006 | strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); | ||
1007 | strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); | ||
1008 | cap->bus_info[0] = '\0'; | ||
1009 | cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; | ||
1010 | |||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh, | ||
1015 | struct v4l2_fmtdesc *fmt) | ||
1016 | { | ||
1017 | if (fmt->index >= NUM_MXC_VOUT_FORMATS) | ||
1018 | return -EINVAL; | ||
1019 | |||
1020 | strlcpy(fmt->description, mxc_formats[fmt->index].description, | ||
1021 | sizeof(fmt->description)); | ||
1022 | fmt->pixelformat = mxc_formats[fmt->index].pixelformat; | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, | ||
1028 | struct v4l2_format *f) | ||
1029 | { | ||
1030 | struct mxc_vout_output *vout = fh; | ||
1031 | struct v4l2_rect rect; | ||
1032 | |||
1033 | f->fmt.pix.width = vout->task.input.width; | ||
1034 | f->fmt.pix.height = vout->task.input.height; | ||
1035 | f->fmt.pix.pixelformat = vout->task.input.format; | ||
1036 | f->fmt.pix.sizeimage = get_frame_size(vout); | ||
1037 | |||
1038 | if (f->fmt.pix.priv) { | ||
1039 | rect.left = vout->task.input.crop.pos.x; | ||
1040 | rect.top = vout->task.input.crop.pos.y; | ||
1041 | rect.width = vout->task.input.crop.w; | ||
1042 | rect.height = vout->task.input.crop.h; | ||
1043 | if (copy_to_user((void __user *)f->fmt.pix.priv, | ||
1044 | &rect, sizeof(rect))) | ||
1045 | return -EFAULT; | ||
1046 | } | ||
1047 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
1048 | "frame_size:0x%x, pix_fmt:0x%x\n", | ||
1049 | f->fmt.pix.sizeimage, | ||
1050 | vout->task.input.format); | ||
1051 | |||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | static inline int ipu_try_task(struct mxc_vout_output *vout) | ||
1056 | { | ||
1057 | int ret; | ||
1058 | struct ipu_task *task = &vout->task; | ||
1059 | |||
1060 | again: | ||
1061 | ret = ipu_check_task(task); | ||
1062 | if (ret != IPU_CHECK_OK) { | ||
1063 | if (ret > IPU_CHECK_ERR_MIN) { | ||
1064 | if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { | ||
1065 | task->input.crop.w -= 8; | ||
1066 | goto again; | ||
1067 | } | ||
1068 | if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { | ||
1069 | task->input.crop.h -= 8; | ||
1070 | goto again; | ||
1071 | } | ||
1072 | if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { | ||
1073 | if (vout->disp_support_windows) { | ||
1074 | task->output.width -= 8; | ||
1075 | task->output.crop.w = | ||
1076 | task->output.width; | ||
1077 | } else | ||
1078 | task->output.crop.w -= 8; | ||
1079 | goto again; | ||
1080 | } | ||
1081 | if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { | ||
1082 | if (vout->disp_support_windows) { | ||
1083 | task->output.height -= 8; | ||
1084 | task->output.crop.h = | ||
1085 | task->output.height; | ||
1086 | } else | ||
1087 | task->output.crop.h -= 8; | ||
1088 | goto again; | ||
1089 | } | ||
1090 | ret = -EINVAL; | ||
1091 | } | ||
1092 | } else | ||
1093 | ret = 0; | ||
1094 | |||
1095 | return ret; | ||
1096 | } | ||
1097 | |||
1098 | static inline int vdoaipu_try_task(struct mxc_vout_output *vout) | ||
1099 | { | ||
1100 | int ret; | ||
1101 | int is_1080p_stream; | ||
1102 | size_t size; | ||
1103 | struct ipu_task *ipu_task = &vout->task; | ||
1104 | struct ipu_crop *icrop = &ipu_task->input.crop; | ||
1105 | struct ipu_task *vdoa_task = &vout->vdoa_task; | ||
1106 | u32 deinterlace = 0; | ||
1107 | u32 in_fmt; | ||
1108 | |||
1109 | if (vout->task.input.deinterlace.enable) | ||
1110 | deinterlace = 1; | ||
1111 | |||
1112 | memset(vdoa_task, 0, sizeof(*vdoa_task)); | ||
1113 | vdoa_task->output.format = IPU_PIX_FMT_NV12; | ||
1114 | memcpy(&vdoa_task->input, &ipu_task->input, | ||
1115 | sizeof(ipu_task->input)); | ||
1116 | if ((icrop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || | ||
1117 | (icrop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { | ||
1118 | vdoa_task->input.crop.w = | ||
1119 | ALIGN(icrop->w, IPU_PIX_FMT_TILED_NV12_MBALIGN); | ||
1120 | vdoa_task->input.crop.h = | ||
1121 | ALIGN(icrop->h, IPU_PIX_FMT_TILED_NV12_MBALIGN); | ||
1122 | } | ||
1123 | vdoa_task->output.width = vdoa_task->input.crop.w; | ||
1124 | vdoa_task->output.height = vdoa_task->input.crop.h; | ||
1125 | vdoa_task->output.crop.w = vdoa_task->input.crop.w; | ||
1126 | vdoa_task->output.crop.h = vdoa_task->input.crop.h; | ||
1127 | |||
1128 | size = PAGE_ALIGN(vdoa_task->input.crop.w * | ||
1129 | vdoa_task->input.crop.h * | ||
1130 | fmt_to_bpp(vdoa_task->output.format)/8); | ||
1131 | if (size > vout->vdoa_work.size) { | ||
1132 | if (vout->vdoa_work.vaddr) | ||
1133 | free_dma_buf(vout, &vout->vdoa_work); | ||
1134 | vout->vdoa_work.size = size; | ||
1135 | ret = alloc_dma_buf(vout, &vout->vdoa_work); | ||
1136 | if (ret < 0) | ||
1137 | return ret; | ||
1138 | } | ||
1139 | ret = ipu_check_task(vdoa_task); | ||
1140 | if (ret != IPU_CHECK_OK) | ||
1141 | return -EINVAL; | ||
1142 | |||
1143 | is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); | ||
1144 | if (is_1080p_stream) | ||
1145 | ipu_task->input.crop.h = VALID_HEIGHT_1080P; | ||
1146 | in_fmt = ipu_task->input.format; | ||
1147 | ipu_task->input.format = vdoa_task->output.format; | ||
1148 | ipu_task->input.height = vdoa_task->output.height; | ||
1149 | ipu_task->input.width = vdoa_task->output.width; | ||
1150 | if (deinterlace) | ||
1151 | ipu_task->input.deinterlace.enable = 0; | ||
1152 | ret = ipu_try_task(vout); | ||
1153 | if (deinterlace) | ||
1154 | ipu_task->input.deinterlace.enable = 1; | ||
1155 | ipu_task->input.format = in_fmt; | ||
1156 | |||
1157 | return ret; | ||
1158 | } | ||
1159 | |||
1160 | static int mxc_vout_try_task(struct mxc_vout_output *vout) | ||
1161 | { | ||
1162 | int ret = 0; | ||
1163 | struct ipu_output *output = &vout->task.output; | ||
1164 | struct ipu_input *input = &vout->task.input; | ||
1165 | struct ipu_crop *crop = &input->crop; | ||
1166 | u32 o_height = 0; | ||
1167 | u32 ocrop_h = 0; | ||
1168 | bool tiled_fmt = false; | ||
1169 | bool tiled_need_pp = false; | ||
1170 | |||
1171 | vout->vdoa_1080p = CHECK_TILED_1080P_DISPLAY(vout); | ||
1172 | if (vout->vdoa_1080p) { | ||
1173 | input->crop.h = FRAME_HEIGHT_1080P; | ||
1174 | o_height = output->height; | ||
1175 | ocrop_h = output->crop.h; | ||
1176 | output->height = FRAME_HEIGHT_1080P; | ||
1177 | output->crop.h = FRAME_HEIGHT_1080P; | ||
1178 | } | ||
1179 | |||
1180 | if ((IPU_PIX_FMT_TILED_NV12 == input->format) || | ||
1181 | (IPU_PIX_FMT_TILED_NV12F == input->format)) { | ||
1182 | if ((input->width % IPU_PIX_FMT_TILED_NV12_MBALIGN) || | ||
1183 | (input->height % IPU_PIX_FMT_TILED_NV12_MBALIGN) || | ||
1184 | (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN) || | ||
1185 | (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { | ||
1186 | v4l2_err(vout->vfd->v4l2_dev, | ||
1187 | "ERR: tiled fmt needs 16 pixel align.\n"); | ||
1188 | return -EINVAL; | ||
1189 | } | ||
1190 | if ((crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || | ||
1191 | (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) | ||
1192 | tiled_need_pp = true; | ||
1193 | } else { | ||
1194 | crop->w -= crop->w % 8; | ||
1195 | crop->h -= crop->h % 8; | ||
1196 | } | ||
1197 | /* assume task.output already set by S_CROP */ | ||
1198 | vout->linear_bypass_pp = is_pp_bypass(vout); | ||
1199 | if (vout->linear_bypass_pp) { | ||
1200 | v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); | ||
1201 | output->format = input->format; | ||
1202 | } else { | ||
1203 | /* if need CSC, choose IPU-DP or IPU_IC do it */ | ||
1204 | if (vout->disp_support_csc) { | ||
1205 | if (colorspaceofpixel(input->format) == YUV_CS) | ||
1206 | output->format = IPU_PIX_FMT_UYVY; | ||
1207 | else | ||
1208 | output->format = IPU_PIX_FMT_RGB565; | ||
1209 | } else { | ||
1210 | if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) | ||
1211 | output->format = IPU_PIX_FMT_UYVY; | ||
1212 | else | ||
1213 | output->format = IPU_PIX_FMT_RGB565; | ||
1214 | } | ||
1215 | |||
1216 | vout->tiled_bypass_pp = false; | ||
1217 | if ((IPU_PIX_FMT_TILED_NV12 == input->format) || | ||
1218 | (IPU_PIX_FMT_TILED_NV12F == input->format)) { | ||
1219 | /* check resize/rotate/flip, or csc task */ | ||
1220 | if (!(tiled_need_pp || | ||
1221 | (IPU_ROTATE_NONE != output->rotate) || | ||
1222 | (input->crop.w != output->crop.w) || | ||
1223 | (input->crop.h != output->crop.h) || | ||
1224 | (!vout->disp_support_csc && | ||
1225 | (colorspaceofpixel(vout->disp_fmt) == RGB_CS))) | ||
1226 | ) { | ||
1227 | /* IC bypass */ | ||
1228 | output->format = IPU_PIX_FMT_NV12; | ||
1229 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
1230 | "tiled bypass pp\n"); | ||
1231 | vout->tiled_bypass_pp = true; | ||
1232 | } | ||
1233 | tiled_fmt = true; | ||
1234 | } | ||
1235 | |||
1236 | if ((!vout->tiled_bypass_pp) && tiled_fmt) | ||
1237 | ret = vdoaipu_try_task(vout); | ||
1238 | else | ||
1239 | ret = ipu_try_task(vout); | ||
1240 | } | ||
1241 | |||
1242 | if (vout->vdoa_1080p) { | ||
1243 | output->height = o_height; | ||
1244 | output->crop.h = ocrop_h; | ||
1245 | } | ||
1246 | |||
1247 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
1248 | "icrop.w:%u, icrop.h:%u, iw:%u, ih:%u," | ||
1249 | "ocrop.w:%u, ocrop.h:%u, ow:%u, oh:%u\n", | ||
1250 | input->crop.w, input->crop.h, | ||
1251 | input->width, input->height, | ||
1252 | output->crop.w, output->crop.h, | ||
1253 | output->width, output->height); | ||
1254 | return ret; | ||
1255 | } | ||
1256 | |||
1257 | static int mxc_vout_try_format(struct mxc_vout_output *vout, | ||
1258 | struct v4l2_format *f) | ||
1259 | { | ||
1260 | int ret = 0; | ||
1261 | struct v4l2_rect rect; | ||
1262 | |||
1263 | if ((f->fmt.pix.field != V4L2_FIELD_NONE) && | ||
1264 | (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)) { | ||
1265 | v4l2_err(vout->vfd->v4l2_dev, | ||
1266 | "progressive tiled fmt should used V4L2_FIELD_NONE!\n"); | ||
1267 | return -EINVAL; | ||
1268 | } | ||
1269 | |||
1270 | if (f->fmt.pix.priv && copy_from_user(&rect, | ||
1271 | (void __user *)f->fmt.pix.priv, sizeof(rect))) | ||
1272 | return -EFAULT; | ||
1273 | |||
1274 | vout->task.input.width = f->fmt.pix.width; | ||
1275 | vout->task.input.height = f->fmt.pix.height; | ||
1276 | vout->task.input.format = f->fmt.pix.pixelformat; | ||
1277 | |||
1278 | ret = set_field_fmt(vout, f->fmt.pix.field); | ||
1279 | if (ret < 0) | ||
1280 | return ret; | ||
1281 | |||
1282 | if (f->fmt.pix.priv) { | ||
1283 | vout->task.input.crop.pos.x = rect.left; | ||
1284 | vout->task.input.crop.pos.y = rect.top; | ||
1285 | vout->task.input.crop.w = rect.width; | ||
1286 | vout->task.input.crop.h = rect.height; | ||
1287 | } else { | ||
1288 | vout->task.input.crop.pos.x = 0; | ||
1289 | vout->task.input.crop.pos.y = 0; | ||
1290 | vout->task.input.crop.w = f->fmt.pix.width; | ||
1291 | vout->task.input.crop.h = f->fmt.pix.height; | ||
1292 | } | ||
1293 | memcpy(&vout->in_rect, &vout->task.input.crop, sizeof(vout->in_rect)); | ||
1294 | |||
1295 | ret = mxc_vout_try_task(vout); | ||
1296 | if (!ret) { | ||
1297 | if (f->fmt.pix.priv) { | ||
1298 | rect.width = vout->task.input.crop.w; | ||
1299 | rect.height = vout->task.input.crop.h; | ||
1300 | if (copy_to_user((void __user *)f->fmt.pix.priv, | ||
1301 | &rect, sizeof(rect))) | ||
1302 | ret = -EFAULT; | ||
1303 | } else { | ||
1304 | f->fmt.pix.width = vout->task.input.crop.w; | ||
1305 | f->fmt.pix.height = vout->task.input.crop.h; | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | return ret; | ||
1310 | } | ||
1311 | |||
1312 | static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh, | ||
1313 | struct v4l2_format *f) | ||
1314 | { | ||
1315 | struct mxc_vout_output *vout = fh; | ||
1316 | int ret = 0; | ||
1317 | |||
1318 | if (vout->vbq.streaming) | ||
1319 | return -EBUSY; | ||
1320 | |||
1321 | mutex_lock(&vout->task_lock); | ||
1322 | ret = mxc_vout_try_format(vout, f); | ||
1323 | if (ret >= 0) | ||
1324 | vout->fmt_init = true; | ||
1325 | mutex_unlock(&vout->task_lock); | ||
1326 | |||
1327 | return ret; | ||
1328 | } | ||
1329 | |||
1330 | static int mxc_vidioc_cropcap(struct file *file, void *fh, | ||
1331 | struct v4l2_cropcap *cropcap) | ||
1332 | { | ||
1333 | struct mxc_vout_output *vout = fh; | ||
1334 | |||
1335 | if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1336 | return -EINVAL; | ||
1337 | |||
1338 | cropcap->bounds = vout->crop_bounds; | ||
1339 | cropcap->defrect = vout->crop_bounds; | ||
1340 | |||
1341 | return 0; | ||
1342 | } | ||
1343 | |||
1344 | static int mxc_vidioc_g_crop(struct file *file, void *fh, | ||
1345 | struct v4l2_crop *crop) | ||
1346 | { | ||
1347 | struct mxc_vout_output *vout = fh; | ||
1348 | |||
1349 | if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1350 | return -EINVAL; | ||
1351 | |||
1352 | if (vout->disp_support_windows) { | ||
1353 | crop->c.left = vout->win_pos.x; | ||
1354 | crop->c.top = vout->win_pos.y; | ||
1355 | crop->c.width = vout->task.output.width; | ||
1356 | crop->c.height = vout->task.output.height; | ||
1357 | } else { | ||
1358 | if (vout->task.output.crop.w && vout->task.output.crop.h) { | ||
1359 | crop->c.left = vout->task.output.crop.pos.x; | ||
1360 | crop->c.top = vout->task.output.crop.pos.y; | ||
1361 | crop->c.width = vout->task.output.crop.w; | ||
1362 | crop->c.height = vout->task.output.crop.h; | ||
1363 | } else { | ||
1364 | crop->c.left = 0; | ||
1365 | crop->c.top = 0; | ||
1366 | crop->c.width = vout->task.output.width; | ||
1367 | crop->c.height = vout->task.output.height; | ||
1368 | } | ||
1369 | } | ||
1370 | |||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | static int mxc_vidioc_s_crop(struct file *file, void *fh, | ||
1375 | const struct v4l2_crop *crop) | ||
1376 | { | ||
1377 | struct mxc_vout_output *vout = fh; | ||
1378 | struct v4l2_rect *b = &vout->crop_bounds; | ||
1379 | struct v4l2_crop fix_up_crop; | ||
1380 | int ret = 0; | ||
1381 | |||
1382 | memcpy(&fix_up_crop, crop, sizeof(*crop)); | ||
1383 | |||
1384 | if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1385 | return -EINVAL; | ||
1386 | |||
1387 | if (crop->c.width < 0 || crop->c.height < 0) | ||
1388 | return -EINVAL; | ||
1389 | |||
1390 | if (crop->c.width == 0) | ||
1391 | fix_up_crop.c.width = b->width - b->left; | ||
1392 | if (crop->c.height == 0) | ||
1393 | fix_up_crop.c.height = b->height - b->top; | ||
1394 | |||
1395 | if (crop->c.top < b->top) | ||
1396 | fix_up_crop.c.top = b->top; | ||
1397 | if (crop->c.top >= b->top + b->height) | ||
1398 | fix_up_crop.c.top = b->top + b->height - 1; | ||
1399 | if (crop->c.height > b->top - crop->c.top + b->height) | ||
1400 | fix_up_crop.c.height = | ||
1401 | b->top - fix_up_crop.c.top + b->height; | ||
1402 | |||
1403 | if (crop->c.left < b->left) | ||
1404 | fix_up_crop.c.left = b->left; | ||
1405 | if (crop->c.left >= b->left + b->width) | ||
1406 | fix_up_crop.c.left = b->left + b->width - 1; | ||
1407 | if (crop->c.width > b->left - crop->c.left + b->width) | ||
1408 | fix_up_crop.c.width = | ||
1409 | b->left - fix_up_crop.c.left + b->width; | ||
1410 | |||
1411 | /* stride line limitation */ | ||
1412 | fix_up_crop.c.height -= fix_up_crop.c.height % 8; | ||
1413 | fix_up_crop.c.width -= fix_up_crop.c.width % 8; | ||
1414 | if ((fix_up_crop.c.width <= 0) || (fix_up_crop.c.height <= 0) || | ||
1415 | ((fix_up_crop.c.left + fix_up_crop.c.width) > | ||
1416 | (b->left + b->width)) || | ||
1417 | ((fix_up_crop.c.top + fix_up_crop.c.height) > | ||
1418 | (b->top + b->height))) { | ||
1419 | v4l2_err(vout->vfd->v4l2_dev, "s_crop err: %d, %d, %d, %d", | ||
1420 | fix_up_crop.c.left, fix_up_crop.c.top, | ||
1421 | fix_up_crop.c.width, fix_up_crop.c.height); | ||
1422 | return -EINVAL; | ||
1423 | } | ||
1424 | |||
1425 | /* the same setting, return */ | ||
1426 | if (vout->disp_support_windows) { | ||
1427 | if ((vout->win_pos.x == fix_up_crop.c.left) && | ||
1428 | (vout->win_pos.y == fix_up_crop.c.top) && | ||
1429 | (vout->task.output.crop.w == fix_up_crop.c.width) && | ||
1430 | (vout->task.output.crop.h == fix_up_crop.c.height)) | ||
1431 | return 0; | ||
1432 | } else { | ||
1433 | if ((vout->task.output.crop.pos.x == fix_up_crop.c.left) && | ||
1434 | (vout->task.output.crop.pos.y == fix_up_crop.c.top) && | ||
1435 | (vout->task.output.crop.w == fix_up_crop.c.width) && | ||
1436 | (vout->task.output.crop.h == fix_up_crop.c.height)) | ||
1437 | return 0; | ||
1438 | } | ||
1439 | |||
1440 | /* wait current work finish */ | ||
1441 | if (vout->vbq.streaming) | ||
1442 | flush_workqueue(vout->v4l_wq); | ||
1443 | |||
1444 | mutex_lock(&vout->task_lock); | ||
1445 | |||
1446 | if (vout->disp_support_windows) { | ||
1447 | vout->task.output.crop.pos.x = 0; | ||
1448 | vout->task.output.crop.pos.y = 0; | ||
1449 | vout->win_pos.x = fix_up_crop.c.left; | ||
1450 | vout->win_pos.y = fix_up_crop.c.top; | ||
1451 | vout->task.output.width = fix_up_crop.c.width; | ||
1452 | vout->task.output.height = fix_up_crop.c.height; | ||
1453 | } else { | ||
1454 | vout->task.output.crop.pos.x = fix_up_crop.c.left; | ||
1455 | vout->task.output.crop.pos.y = fix_up_crop.c.top; | ||
1456 | } | ||
1457 | |||
1458 | vout->task.output.crop.w = fix_up_crop.c.width; | ||
1459 | vout->task.output.crop.h = fix_up_crop.c.height; | ||
1460 | |||
1461 | /* | ||
1462 | * must S_CROP before S_FMT, for fist time S_CROP, will not check | ||
1463 | * ipu task, it will check in S_FMT, after S_FMT, S_CROP should | ||
1464 | * check ipu task too. | ||
1465 | */ | ||
1466 | if (vout->fmt_init) { | ||
1467 | if (vout->vbq.streaming) | ||
1468 | release_disp_output(vout); | ||
1469 | |||
1470 | memcpy(&vout->task.input.crop, &vout->in_rect, | ||
1471 | sizeof(vout->in_rect)); | ||
1472 | ret = mxc_vout_try_task(vout); | ||
1473 | if (ret < 0) { | ||
1474 | v4l2_err(vout->vfd->v4l2_dev, | ||
1475 | "vout check task failed\n"); | ||
1476 | goto done; | ||
1477 | } | ||
1478 | if (vout->vbq.streaming) { | ||
1479 | ret = config_disp_output(vout); | ||
1480 | if (ret < 0) { | ||
1481 | v4l2_err(vout->vfd->v4l2_dev, | ||
1482 | "Config display output failed\n"); | ||
1483 | goto done; | ||
1484 | } | ||
1485 | } | ||
1486 | } | ||
1487 | |||
1488 | done: | ||
1489 | mutex_unlock(&vout->task_lock); | ||
1490 | |||
1491 | return ret; | ||
1492 | } | ||
1493 | |||
1494 | static int mxc_vidioc_queryctrl(struct file *file, void *fh, | ||
1495 | struct v4l2_queryctrl *ctrl) | ||
1496 | { | ||
1497 | int ret = 0; | ||
1498 | |||
1499 | switch (ctrl->id) { | ||
1500 | case V4L2_CID_ROTATE: | ||
1501 | ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); | ||
1502 | break; | ||
1503 | case V4L2_CID_VFLIP: | ||
1504 | ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); | ||
1505 | break; | ||
1506 | case V4L2_CID_HFLIP: | ||
1507 | ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); | ||
1508 | break; | ||
1509 | case V4L2_CID_MXC_MOTION: | ||
1510 | ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0); | ||
1511 | break; | ||
1512 | default: | ||
1513 | ctrl->name[0] = '\0'; | ||
1514 | ret = -EINVAL; | ||
1515 | } | ||
1516 | return ret; | ||
1517 | } | ||
1518 | |||
1519 | static int mxc_vidioc_g_ctrl(struct file *file, void *fh, | ||
1520 | struct v4l2_control *ctrl) | ||
1521 | { | ||
1522 | int ret = 0; | ||
1523 | struct mxc_vout_output *vout = fh; | ||
1524 | |||
1525 | switch (ctrl->id) { | ||
1526 | case V4L2_CID_ROTATE: | ||
1527 | ctrl->value = vout->ctrl_rotate; | ||
1528 | break; | ||
1529 | case V4L2_CID_VFLIP: | ||
1530 | ctrl->value = vout->ctrl_vflip; | ||
1531 | break; | ||
1532 | case V4L2_CID_HFLIP: | ||
1533 | ctrl->value = vout->ctrl_hflip; | ||
1534 | break; | ||
1535 | case V4L2_CID_MXC_MOTION: | ||
1536 | if (vout->task.input.deinterlace.enable) | ||
1537 | ctrl->value = vout->task.input.deinterlace.motion; | ||
1538 | else | ||
1539 | ctrl->value = 0; | ||
1540 | break; | ||
1541 | default: | ||
1542 | ret = -EINVAL; | ||
1543 | } | ||
1544 | return ret; | ||
1545 | } | ||
1546 | |||
1547 | static void setup_task_rotation(struct mxc_vout_output *vout) | ||
1548 | { | ||
1549 | if (vout->ctrl_rotate == 0) { | ||
1550 | if (vout->ctrl_vflip && vout->ctrl_hflip) | ||
1551 | vout->task.output.rotate = IPU_ROTATE_180; | ||
1552 | else if (vout->ctrl_vflip) | ||
1553 | vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; | ||
1554 | else if (vout->ctrl_hflip) | ||
1555 | vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; | ||
1556 | else | ||
1557 | vout->task.output.rotate = IPU_ROTATE_NONE; | ||
1558 | } else if (vout->ctrl_rotate == 90) { | ||
1559 | if (vout->ctrl_vflip && vout->ctrl_hflip) | ||
1560 | vout->task.output.rotate = IPU_ROTATE_90_LEFT; | ||
1561 | else if (vout->ctrl_vflip) | ||
1562 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; | ||
1563 | else if (vout->ctrl_hflip) | ||
1564 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; | ||
1565 | else | ||
1566 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT; | ||
1567 | } else if (vout->ctrl_rotate == 180) { | ||
1568 | if (vout->ctrl_vflip && vout->ctrl_hflip) | ||
1569 | vout->task.output.rotate = IPU_ROTATE_NONE; | ||
1570 | else if (vout->ctrl_vflip) | ||
1571 | vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; | ||
1572 | else if (vout->ctrl_hflip) | ||
1573 | vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; | ||
1574 | else | ||
1575 | vout->task.output.rotate = IPU_ROTATE_180; | ||
1576 | } else if (vout->ctrl_rotate == 270) { | ||
1577 | if (vout->ctrl_vflip && vout->ctrl_hflip) | ||
1578 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT; | ||
1579 | else if (vout->ctrl_vflip) | ||
1580 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; | ||
1581 | else if (vout->ctrl_hflip) | ||
1582 | vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; | ||
1583 | else | ||
1584 | vout->task.output.rotate = IPU_ROTATE_90_LEFT; | ||
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | static int mxc_vidioc_s_ctrl(struct file *file, void *fh, | ||
1589 | struct v4l2_control *ctrl) | ||
1590 | { | ||
1591 | int ret = 0; | ||
1592 | struct mxc_vout_output *vout = fh; | ||
1593 | |||
1594 | /* wait current work finish */ | ||
1595 | if (vout->vbq.streaming) | ||
1596 | flush_workqueue(vout->v4l_wq); | ||
1597 | |||
1598 | mutex_lock(&vout->task_lock); | ||
1599 | switch (ctrl->id) { | ||
1600 | case V4L2_CID_ROTATE: | ||
1601 | { | ||
1602 | vout->ctrl_rotate = (ctrl->value/90) * 90; | ||
1603 | if (vout->ctrl_rotate > 270) | ||
1604 | vout->ctrl_rotate = 270; | ||
1605 | setup_task_rotation(vout); | ||
1606 | break; | ||
1607 | } | ||
1608 | case V4L2_CID_VFLIP: | ||
1609 | { | ||
1610 | vout->ctrl_vflip = ctrl->value; | ||
1611 | setup_task_rotation(vout); | ||
1612 | break; | ||
1613 | } | ||
1614 | case V4L2_CID_HFLIP: | ||
1615 | { | ||
1616 | vout->ctrl_hflip = ctrl->value; | ||
1617 | setup_task_rotation(vout); | ||
1618 | break; | ||
1619 | } | ||
1620 | case V4L2_CID_MXC_MOTION: | ||
1621 | { | ||
1622 | vout->task.input.deinterlace.motion = ctrl->value; | ||
1623 | break; | ||
1624 | } | ||
1625 | default: | ||
1626 | ret = -EINVAL; | ||
1627 | goto done; | ||
1628 | } | ||
1629 | |||
1630 | if (vout->fmt_init) { | ||
1631 | if (vout->vbq.streaming) | ||
1632 | release_disp_output(vout); | ||
1633 | |||
1634 | memcpy(&vout->task.input.crop, &vout->in_rect, | ||
1635 | sizeof(vout->in_rect)); | ||
1636 | ret = mxc_vout_try_task(vout); | ||
1637 | if (ret < 0) { | ||
1638 | v4l2_err(vout->vfd->v4l2_dev, | ||
1639 | "vout check task failed\n"); | ||
1640 | goto done; | ||
1641 | } | ||
1642 | if (vout->vbq.streaming) { | ||
1643 | ret = config_disp_output(vout); | ||
1644 | if (ret < 0) { | ||
1645 | v4l2_err(vout->vfd->v4l2_dev, | ||
1646 | "Config display output failed\n"); | ||
1647 | goto done; | ||
1648 | } | ||
1649 | } | ||
1650 | } | ||
1651 | |||
1652 | done: | ||
1653 | mutex_unlock(&vout->task_lock); | ||
1654 | |||
1655 | return ret; | ||
1656 | } | ||
1657 | |||
1658 | static int mxc_vidioc_reqbufs(struct file *file, void *fh, | ||
1659 | struct v4l2_requestbuffers *req) | ||
1660 | { | ||
1661 | int ret = 0; | ||
1662 | struct mxc_vout_output *vout = fh; | ||
1663 | struct videobuf_queue *q = &vout->vbq; | ||
1664 | |||
1665 | if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1666 | return -EINVAL; | ||
1667 | |||
1668 | /* should not be here after streaming, videobuf_reqbufs will control */ | ||
1669 | mutex_lock(&vout->task_lock); | ||
1670 | |||
1671 | ret = videobuf_reqbufs(q, req); | ||
1672 | |||
1673 | mutex_unlock(&vout->task_lock); | ||
1674 | return ret; | ||
1675 | } | ||
1676 | |||
1677 | static int mxc_vidioc_querybuf(struct file *file, void *fh, | ||
1678 | struct v4l2_buffer *b) | ||
1679 | { | ||
1680 | int ret; | ||
1681 | struct mxc_vout_output *vout = fh; | ||
1682 | |||
1683 | ret = videobuf_querybuf(&vout->vbq, b); | ||
1684 | if (!ret) { | ||
1685 | /* return physical address */ | ||
1686 | struct videobuf_buffer *vb = vout->vbq.bufs[b->index]; | ||
1687 | if (b->flags & V4L2_BUF_FLAG_MAPPED) | ||
1688 | b->m.offset = videobuf_to_dma_contig(vb); | ||
1689 | } | ||
1690 | |||
1691 | return ret; | ||
1692 | } | ||
1693 | |||
1694 | static int mxc_vidioc_qbuf(struct file *file, void *fh, | ||
1695 | struct v4l2_buffer *buffer) | ||
1696 | { | ||
1697 | struct mxc_vout_output *vout = fh; | ||
1698 | |||
1699 | return videobuf_qbuf(&vout->vbq, buffer); | ||
1700 | } | ||
1701 | |||
1702 | static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) | ||
1703 | { | ||
1704 | struct mxc_vout_output *vout = fh; | ||
1705 | |||
1706 | if (!vout->vbq.streaming) | ||
1707 | return -EINVAL; | ||
1708 | |||
1709 | if (file->f_flags & O_NONBLOCK) | ||
1710 | return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1); | ||
1711 | else | ||
1712 | return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0); | ||
1713 | } | ||
1714 | |||
1715 | static int set_window_position(struct mxc_vout_output *vout, | ||
1716 | struct mxcfb_pos *pos) | ||
1717 | { | ||
1718 | struct fb_info *fbi = vout->fbi; | ||
1719 | mm_segment_t old_fs; | ||
1720 | int ret = 0; | ||
1721 | |||
1722 | if (vout->disp_support_windows) { | ||
1723 | old_fs = get_fs(); | ||
1724 | set_fs(KERNEL_DS); | ||
1725 | ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, | ||
1726 | (unsigned long)pos); | ||
1727 | set_fs(old_fs); | ||
1728 | } | ||
1729 | |||
1730 | return ret; | ||
1731 | } | ||
1732 | |||
1733 | static int config_disp_output(struct mxc_vout_output *vout) | ||
1734 | { | ||
1735 | struct dma_mem *buf = NULL; | ||
1736 | struct fb_info *fbi = vout->fbi; | ||
1737 | struct fb_var_screeninfo var; | ||
1738 | int i, fb_num, ret; | ||
1739 | u32 fb_base; | ||
1740 | u32 size; | ||
1741 | u32 display_buf_size; | ||
1742 | u32 *pixel = NULL; | ||
1743 | u32 color; | ||
1744 | int j; | ||
1745 | |||
1746 | memcpy(&var, &fbi->var, sizeof(var)); | ||
1747 | fb_base = fbi->fix.smem_start; | ||
1748 | |||
1749 | var.xres = vout->task.output.width; | ||
1750 | var.yres = vout->task.output.height; | ||
1751 | if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { | ||
1752 | fb_num = 1; | ||
1753 | /* input crop */ | ||
1754 | if (vout->task.input.width > vout->task.output.width) | ||
1755 | var.xres_virtual = vout->task.input.width; | ||
1756 | else | ||
1757 | var.xres_virtual = var.xres; | ||
1758 | if (vout->task.input.height > vout->task.output.height) | ||
1759 | var.yres_virtual = vout->task.input.height; | ||
1760 | else | ||
1761 | var.yres_virtual = var.yres; | ||
1762 | var.rotate = vout->task.output.rotate; | ||
1763 | var.vmode |= FB_VMODE_YWRAP; | ||
1764 | } else { | ||
1765 | fb_num = FB_BUFS; | ||
1766 | var.xres_virtual = var.xres; | ||
1767 | var.yres_virtual = fb_num * var.yres; | ||
1768 | var.vmode &= ~FB_VMODE_YWRAP; | ||
1769 | } | ||
1770 | var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); | ||
1771 | var.nonstd = vout->task.output.format; | ||
1772 | |||
1773 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
1774 | "set display fb to %d %d\n", | ||
1775 | var.xres, var.yres); | ||
1776 | |||
1777 | ret = set_window_position(vout, &vout->win_pos); | ||
1778 | if (ret < 0) { | ||
1779 | v4l2_err(vout->vfd->v4l2_dev, "ERR: set_win_pos ret:%d\n", ret); | ||
1780 | return ret; | ||
1781 | } | ||
1782 | |||
1783 | /* Init display channel through fb API */ | ||
1784 | var.yoffset = 0; | ||
1785 | var.activate |= FB_ACTIVATE_FORCE; | ||
1786 | console_lock(); | ||
1787 | fbi->flags |= FBINFO_MISC_USEREVENT; | ||
1788 | ret = fb_set_var(fbi, &var); | ||
1789 | fbi->flags &= ~FBINFO_MISC_USEREVENT; | ||
1790 | console_unlock(); | ||
1791 | if (ret < 0) { | ||
1792 | v4l2_err(vout->vfd->v4l2_dev, | ||
1793 | "ERR:%s fb_set_var ret:%d\n", __func__, ret); | ||
1794 | return ret; | ||
1795 | } | ||
1796 | if (vout->linear_bypass_pp || vout->tiled_bypass_pp) | ||
1797 | display_buf_size = fbi->fix.line_length * fbi->var.yres_virtual; | ||
1798 | else | ||
1799 | display_buf_size = fbi->fix.line_length * fbi->var.yres; | ||
1800 | for (i = 0; i < fb_num; i++) | ||
1801 | vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; | ||
1802 | if (vout->tiled_bypass_pp) { | ||
1803 | size = PAGE_ALIGN(vout->task.input.crop.w * | ||
1804 | vout->task.input.crop.h * | ||
1805 | fmt_to_bpp(vout->task.output.format)/8); | ||
1806 | if (size > vout->vdoa_output[0].size) { | ||
1807 | for (i = 0; i < VDOA_FB_BUFS; i++) { | ||
1808 | buf = &vout->vdoa_output[i]; | ||
1809 | if (buf->vaddr) | ||
1810 | free_dma_buf(vout, buf); | ||
1811 | buf->size = size; | ||
1812 | ret = alloc_dma_buf(vout, buf); | ||
1813 | if (ret < 0) | ||
1814 | goto err; | ||
1815 | } | ||
1816 | } | ||
1817 | for (i = fb_num; i < (fb_num + VDOA_FB_BUFS); i++) | ||
1818 | vout->disp_bufs[i] = | ||
1819 | vout->vdoa_output[i - fb_num].paddr; | ||
1820 | } | ||
1821 | vout->fb_smem_len = fbi->fix.smem_len; | ||
1822 | vout->fb_smem_start = fbi->fix.smem_start; | ||
1823 | if (fb_base != fbi->fix.smem_start) { | ||
1824 | v4l2_dbg(1, debug, vout->vfd->v4l2_dev, | ||
1825 | "realloc fb mem size:0x%x@0x%lx,old paddr @0x%x\n", | ||
1826 | fbi->fix.smem_len, fbi->fix.smem_start, fb_base); | ||
1827 | } | ||
1828 | |||
1829 | /* fill black when video config changed */ | ||
1830 | color = colorspaceofpixel(vout->task.output.format) == YUV_CS ? | ||
1831 | UYVY_BLACK : RGB_BLACK; | ||
1832 | if (IS_PLANAR_PIXEL_FORMAT(vout->task.output.format)) { | ||
1833 | size = display_buf_size * 8 / | ||
1834 | fmt_to_bpp(vout->task.output.format); | ||
1835 | memset(fbi->screen_base, Y_BLACK, size); | ||
1836 | memset(fbi->screen_base + size, UV_BLACK, | ||
1837 | display_buf_size - size); | ||
1838 | } else { | ||
1839 | pixel = (u32 *)fbi->screen_base; | ||
1840 | for (i = 0; i < (display_buf_size >> 2); i++) | ||
1841 | *pixel++ = color; | ||
1842 | } | ||
1843 | console_lock(); | ||
1844 | fbi->flags |= FBINFO_MISC_USEREVENT; | ||
1845 | ret = fb_blank(fbi, FB_BLANK_UNBLANK); | ||
1846 | fbi->flags &= ~FBINFO_MISC_USEREVENT; | ||
1847 | console_unlock(); | ||
1848 | vout->release = false; | ||
1849 | |||
1850 | return ret; | ||
1851 | err: | ||
1852 | for (j = i - 1; j >= 0; j--) { | ||
1853 | buf = &vout->vdoa_output[j]; | ||
1854 | if (buf->vaddr) | ||
1855 | free_dma_buf(vout, buf); | ||
1856 | } | ||
1857 | return ret; | ||
1858 | } | ||
1859 | |||
1860 | static inline void wait_for_vsync(struct mxc_vout_output *vout) | ||
1861 | { | ||
1862 | struct fb_info *fbi = vout->fbi; | ||
1863 | mm_segment_t old_fs; | ||
1864 | |||
1865 | if (fbi->fbops->fb_ioctl) { | ||
1866 | old_fs = get_fs(); | ||
1867 | set_fs(KERNEL_DS); | ||
1868 | fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, | ||
1869 | (unsigned long)NULL); | ||
1870 | set_fs(old_fs); | ||
1871 | } | ||
1872 | |||
1873 | return; | ||
1874 | } | ||
1875 | |||
1876 | static void release_disp_output(struct mxc_vout_output *vout) | ||
1877 | { | ||
1878 | struct fb_info *fbi = vout->fbi; | ||
1879 | struct mxcfb_pos pos; | ||
1880 | |||
1881 | if (vout->release) | ||
1882 | return; | ||
1883 | console_lock(); | ||
1884 | fbi->flags |= FBINFO_MISC_USEREVENT; | ||
1885 | fb_blank(fbi, FB_BLANK_POWERDOWN); | ||
1886 | fbi->flags &= ~FBINFO_MISC_USEREVENT; | ||
1887 | console_unlock(); | ||
1888 | |||
1889 | /* restore pos to 0,0 avoid fb pan display hang? */ | ||
1890 | pos.x = 0; | ||
1891 | pos.y = 0; | ||
1892 | set_window_position(vout, &pos); | ||
1893 | |||
1894 | if (get_ipu_channel(fbi) == MEM_BG_SYNC) { | ||
1895 | console_lock(); | ||
1896 | fbi->fix.smem_start = vout->disp_bufs[0]; | ||
1897 | fbi->flags |= FBINFO_MISC_USEREVENT; | ||
1898 | fb_blank(fbi, FB_BLANK_UNBLANK); | ||
1899 | fbi->flags &= ~FBINFO_MISC_USEREVENT; | ||
1900 | console_unlock(); | ||
1901 | |||
1902 | } | ||
1903 | |||
1904 | vout->release = true; | ||
1905 | } | ||
1906 | |||
1907 | static int mxc_vidioc_streamon(struct file *file, void *fh, | ||
1908 | enum v4l2_buf_type i) | ||
1909 | { | ||
1910 | struct mxc_vout_output *vout = fh; | ||
1911 | struct videobuf_queue *q = &vout->vbq; | ||
1912 | int ret; | ||
1913 | |||
1914 | if (q->streaming) { | ||
1915 | v4l2_err(vout->vfd->v4l2_dev, | ||
1916 | "video output already run\n"); | ||
1917 | ret = -EBUSY; | ||
1918 | goto done; | ||
1919 | } | ||
1920 | |||
1921 | if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) { | ||
1922 | v4l2_err(vout->vfd->v4l2_dev, | ||
1923 | "deinterlacing: need queue 2 frame before streamon\n"); | ||
1924 | ret = -EINVAL; | ||
1925 | goto done; | ||
1926 | } | ||
1927 | |||
1928 | ret = config_disp_output(vout); | ||
1929 | if (ret < 0) { | ||
1930 | v4l2_err(vout->vfd->v4l2_dev, | ||
1931 | "Config display output failed\n"); | ||
1932 | goto done; | ||
1933 | } | ||
1934 | |||
1935 | hrtimer_init(&vout->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); | ||
1936 | vout->timer.function = mxc_vout_timer_handler; | ||
1937 | vout->timer_stop = true; | ||
1938 | |||
1939 | vout->start_ktime = hrtimer_cb_get_time(&vout->timer); | ||
1940 | |||
1941 | vout->pre1_vb = NULL; | ||
1942 | vout->pre2_vb = NULL; | ||
1943 | |||
1944 | ret = videobuf_streamon(q); | ||
1945 | done: | ||
1946 | return ret; | ||
1947 | } | ||
1948 | |||
1949 | static int mxc_vidioc_streamoff(struct file *file, void *fh, | ||
1950 | enum v4l2_buf_type i) | ||
1951 | { | ||
1952 | struct mxc_vout_output *vout = fh; | ||
1953 | struct videobuf_queue *q = &vout->vbq; | ||
1954 | int ret = 0; | ||
1955 | |||
1956 | if (q->streaming) { | ||
1957 | flush_workqueue(vout->v4l_wq); | ||
1958 | |||
1959 | hrtimer_cancel(&vout->timer); | ||
1960 | |||
1961 | /* | ||
1962 | * Wait for 2 vsyncs to make sure | ||
1963 | * frames are drained on triple | ||
1964 | * buffer. | ||
1965 | */ | ||
1966 | wait_for_vsync(vout); | ||
1967 | wait_for_vsync(vout); | ||
1968 | |||
1969 | release_disp_output(vout); | ||
1970 | |||
1971 | ret = videobuf_streamoff(&vout->vbq); | ||
1972 | } | ||
1973 | INIT_LIST_HEAD(&vout->queue_list); | ||
1974 | INIT_LIST_HEAD(&vout->active_list); | ||
1975 | |||
1976 | return ret; | ||
1977 | } | ||
1978 | |||
1979 | static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = { | ||
1980 | .vidioc_querycap = mxc_vidioc_querycap, | ||
1981 | .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out, | ||
1982 | .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out, | ||
1983 | .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out, | ||
1984 | .vidioc_cropcap = mxc_vidioc_cropcap, | ||
1985 | .vidioc_g_crop = mxc_vidioc_g_crop, | ||
1986 | .vidioc_s_crop = mxc_vidioc_s_crop, | ||
1987 | .vidioc_queryctrl = mxc_vidioc_queryctrl, | ||
1988 | .vidioc_g_ctrl = mxc_vidioc_g_ctrl, | ||
1989 | .vidioc_s_ctrl = mxc_vidioc_s_ctrl, | ||
1990 | .vidioc_reqbufs = mxc_vidioc_reqbufs, | ||
1991 | .vidioc_querybuf = mxc_vidioc_querybuf, | ||
1992 | .vidioc_qbuf = mxc_vidioc_qbuf, | ||
1993 | .vidioc_dqbuf = mxc_vidioc_dqbuf, | ||
1994 | .vidioc_streamon = mxc_vidioc_streamon, | ||
1995 | .vidioc_streamoff = mxc_vidioc_streamoff, | ||
1996 | }; | ||
1997 | |||
1998 | static const struct v4l2_file_operations mxc_vout_fops = { | ||
1999 | .owner = THIS_MODULE, | ||
2000 | .unlocked_ioctl = video_ioctl2, | ||
2001 | .mmap = mxc_vout_mmap, | ||
2002 | .open = mxc_vout_open, | ||
2003 | .release = mxc_vout_release, | ||
2004 | }; | ||
2005 | |||
2006 | static struct video_device mxc_vout_template = { | ||
2007 | .name = "MXC Video Output", | ||
2008 | .fops = &mxc_vout_fops, | ||
2009 | .ioctl_ops = &mxc_vout_ioctl_ops, | ||
2010 | .release = video_device_release, | ||
2011 | }; | ||
2012 | |||
2013 | static struct videobuf_queue_ops mxc_vout_vbq_ops = { | ||
2014 | .buf_setup = mxc_vout_buffer_setup, | ||
2015 | .buf_prepare = mxc_vout_buffer_prepare, | ||
2016 | .buf_release = mxc_vout_buffer_release, | ||
2017 | .buf_queue = mxc_vout_buffer_queue, | ||
2018 | }; | ||
2019 | |||
2020 | static void mxc_vout_free_output(struct mxc_vout_dev *dev) | ||
2021 | { | ||
2022 | int i; | ||
2023 | int j; | ||
2024 | struct mxc_vout_output *vout; | ||
2025 | struct video_device *vfd; | ||
2026 | |||
2027 | for (i = 0; i < dev->out_num; i++) { | ||
2028 | vout = dev->out[i]; | ||
2029 | vfd = vout->vfd; | ||
2030 | if (vout->vdoa_work.vaddr) | ||
2031 | free_dma_buf(vout, &vout->vdoa_work); | ||
2032 | for (j = 0; j < VDOA_FB_BUFS; j++) { | ||
2033 | if (vout->vdoa_output[j].vaddr) | ||
2034 | free_dma_buf(vout, &vout->vdoa_output[j]); | ||
2035 | } | ||
2036 | if (vfd) { | ||
2037 | if (!video_is_registered(vfd)) | ||
2038 | video_device_release(vfd); | ||
2039 | else | ||
2040 | video_unregister_device(vfd); | ||
2041 | } | ||
2042 | kfree(vout); | ||
2043 | } | ||
2044 | } | ||
2045 | |||
2046 | static int mxc_vout_setup_output(struct mxc_vout_dev *dev) | ||
2047 | { | ||
2048 | struct videobuf_queue *q; | ||
2049 | struct fb_info *fbi; | ||
2050 | struct mxc_vout_output *vout; | ||
2051 | int i, ret = 0; | ||
2052 | |||
2053 | update_display_setting(); | ||
2054 | |||
2055 | /* all output/overlay based on fb */ | ||
2056 | for (i = 0; i < num_registered_fb; i++) { | ||
2057 | fbi = registered_fb[i]; | ||
2058 | |||
2059 | vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL); | ||
2060 | if (!vout) { | ||
2061 | ret = -ENOMEM; | ||
2062 | break; | ||
2063 | } | ||
2064 | |||
2065 | dev->out[dev->out_num] = vout; | ||
2066 | dev->out_num++; | ||
2067 | |||
2068 | vout->fbi = fbi; | ||
2069 | vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
2070 | vout->vfd = video_device_alloc(); | ||
2071 | if (!vout->vfd) { | ||
2072 | ret = -ENOMEM; | ||
2073 | break; | ||
2074 | } | ||
2075 | |||
2076 | *vout->vfd = mxc_vout_template; | ||
2077 | vout->vfd->debug = debug; | ||
2078 | vout->vfd->v4l2_dev = &dev->v4l2_dev; | ||
2079 | vout->vfd->lock = &vout->mutex; | ||
2080 | vout->vfd->vfl_dir = VFL_DIR_TX; | ||
2081 | |||
2082 | mutex_init(&vout->mutex); | ||
2083 | mutex_init(&vout->task_lock); | ||
2084 | |||
2085 | strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); | ||
2086 | |||
2087 | video_set_drvdata(vout->vfd, vout); | ||
2088 | |||
2089 | if (video_register_device(vout->vfd, | ||
2090 | VFL_TYPE_GRABBER, video_nr + i) < 0) { | ||
2091 | ret = -ENODEV; | ||
2092 | break; | ||
2093 | } | ||
2094 | |||
2095 | q = &vout->vbq; | ||
2096 | q->dev = dev->dev; | ||
2097 | spin_lock_init(&vout->vbq_lock); | ||
2098 | videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev, | ||
2099 | &vout->vbq_lock, vout->type, V4L2_FIELD_NONE, | ||
2100 | sizeof(struct videobuf_buffer), vout, NULL); | ||
2101 | |||
2102 | v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n", | ||
2103 | video_device_node_name(vout->vfd)); | ||
2104 | |||
2105 | } | ||
2106 | |||
2107 | return ret; | ||
2108 | } | ||
2109 | |||
2110 | static int mxc_vout_probe(struct platform_device *pdev) | ||
2111 | { | ||
2112 | int ret; | ||
2113 | struct mxc_vout_dev *dev; | ||
2114 | |||
2115 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
2116 | if (!dev) | ||
2117 | return -ENOMEM; | ||
2118 | |||
2119 | dev->dev = &pdev->dev; | ||
2120 | dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL); | ||
2121 | *dev->dev->dma_mask = DMA_BIT_MASK(32); | ||
2122 | dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
2123 | |||
2124 | ret = v4l2_device_register(dev->dev, &dev->v4l2_dev); | ||
2125 | if (ret) { | ||
2126 | dev_err(dev->dev, "v4l2_device_register failed\n"); | ||
2127 | goto free_dev; | ||
2128 | } | ||
2129 | |||
2130 | ret = mxc_vout_setup_output(dev); | ||
2131 | if (ret < 0) | ||
2132 | goto rel_vdev; | ||
2133 | |||
2134 | return 0; | ||
2135 | |||
2136 | rel_vdev: | ||
2137 | mxc_vout_free_output(dev); | ||
2138 | v4l2_device_unregister(&dev->v4l2_dev); | ||
2139 | free_dev: | ||
2140 | kfree(dev); | ||
2141 | return ret; | ||
2142 | } | ||
2143 | |||
2144 | static int mxc_vout_remove(struct platform_device *pdev) | ||
2145 | { | ||
2146 | struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); | ||
2147 | struct mxc_vout_dev *dev = container_of(v4l2_dev, struct | ||
2148 | mxc_vout_dev, v4l2_dev); | ||
2149 | |||
2150 | mxc_vout_free_output(dev); | ||
2151 | v4l2_device_unregister(v4l2_dev); | ||
2152 | kfree(dev); | ||
2153 | return 0; | ||
2154 | } | ||
2155 | |||
2156 | static const struct of_device_id mxc_v4l2_dt_ids[] = { | ||
2157 | { .compatible = "fsl,mxc_v4l2_output", }, | ||
2158 | { /* sentinel */ } | ||
2159 | }; | ||
2160 | |||
2161 | static struct platform_driver mxc_vout_driver = { | ||
2162 | .driver = { | ||
2163 | .name = "mxc_v4l2_output", | ||
2164 | .of_match_table = mxc_v4l2_dt_ids, | ||
2165 | }, | ||
2166 | .probe = mxc_vout_probe, | ||
2167 | .remove = mxc_vout_remove, | ||
2168 | }; | ||
2169 | |||
2170 | static int __init mxc_vout_init(void) | ||
2171 | { | ||
2172 | if (platform_driver_register(&mxc_vout_driver) != 0) { | ||
2173 | printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); | ||
2174 | return -EINVAL; | ||
2175 | } | ||
2176 | return 0; | ||
2177 | } | ||
2178 | |||
2179 | static void mxc_vout_cleanup(void) | ||
2180 | { | ||
2181 | platform_driver_unregister(&mxc_vout_driver); | ||
2182 | } | ||
2183 | |||
2184 | module_init(mxc_vout_init); | ||
2185 | module_exit(mxc_vout_cleanup); | ||
2186 | |||
2187 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
2188 | MODULE_DESCRIPTION("V4L2-driver for MXC video output"); | ||
2189 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/mxc_v4l2.h b/include/linux/mxc_v4l2.h new file mode 100644 index 000000000000..e05bd043496c --- /dev/null +++ b/include/linux/mxc_v4l2.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * The code contained herein is licensed under the GNU Lesser General | ||
7 | * Public License. You may obtain a copy of the GNU Lesser General | ||
8 | * Public License Version 2.1 or later at the following locations: | ||
9 | * | ||
10 | * http://www.opensource.org/licenses/lgpl-license.html | ||
11 | * http://www.gnu.org/copyleft/lgpl.html | ||
12 | */ | ||
13 | |||
14 | /*! | ||
15 | * @file linux/mxc_v4l2.h | ||
16 | * | ||
17 | * @brief MXC V4L2 private header file | ||
18 | * | ||
19 | * @ingroup MXC V4L2 | ||
20 | */ | ||
21 | |||
22 | #ifndef __LINUX_MXC_V4L2_H__ | ||
23 | #define __LINUX_MXC_V4L2_H__ | ||
24 | |||
25 | #include <uapi/linux/mxc_v4l2.h> | ||
26 | |||
27 | #endif | ||
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index a19cdb19899d..9c9eb267483c 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild | |||
@@ -251,6 +251,7 @@ header-y += msdos_fs.h | |||
251 | header-y += msg.h | 251 | header-y += msg.h |
252 | header-y += mtio.h | 252 | header-y += mtio.h |
253 | header-y += mxcfb.h | 253 | header-y += mxcfb.h |
254 | header-y += mxc_v4l2.h | ||
254 | header-y += n_r3964.h | 255 | header-y += n_r3964.h |
255 | header-y += nbd.h | 256 | header-y += nbd.h |
256 | header-y += ncp.h | 257 | header-y += ncp.h |
diff --git a/include/uapi/linux/mxc_v4l2.h b/include/uapi/linux/mxc_v4l2.h new file mode 100644 index 000000000000..49345fea15ec --- /dev/null +++ b/include/uapi/linux/mxc_v4l2.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | /*! | ||
22 | * @file uapi/linux/mxc_v4l2.h | ||
23 | * | ||
24 | * @brief MXC V4L2 private header file | ||
25 | * | ||
26 | * @ingroup MXC V4L2 | ||
27 | */ | ||
28 | |||
29 | #ifndef __ASM_ARCH_MXC_V4L2_H__ | ||
30 | #define __ASM_ARCH_MXC_V4L2_H__ | ||
31 | |||
32 | /* | ||
33 | * For IPUv1 and IPUv3, V4L2_CID_MXC_ROT means encoder ioctl ID. | ||
34 | * And V4L2_CID_MXC_VF_ROT is viewfinder ioctl ID only for IPUv1 and IPUv3. | ||
35 | */ | ||
36 | #define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0) | ||
37 | #define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1) | ||
38 | #define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2) | ||
39 | #define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3) | ||
40 | #define V4L2_CID_MXC_SWITCH_CAM (V4L2_CID_PRIVATE_BASE + 6) | ||
41 | |||
42 | #define V4L2_MXC_ROTATE_NONE 0 | ||
43 | #define V4L2_MXC_ROTATE_VERT_FLIP 1 | ||
44 | #define V4L2_MXC_ROTATE_HORIZ_FLIP 2 | ||
45 | #define V4L2_MXC_ROTATE_180 3 | ||
46 | #define V4L2_MXC_ROTATE_90_RIGHT 4 | ||
47 | #define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5 | ||
48 | #define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6 | ||
49 | #define V4L2_MXC_ROTATE_90_LEFT 7 | ||
50 | |||
51 | struct v4l2_mxc_offset { | ||
52 | uint32_t u_offset; | ||
53 | uint32_t v_offset; | ||
54 | }; | ||
55 | |||
56 | #endif | ||