Commit | Line | Data |
---|---|---|
fef1c8d0 TS |
1 | /* |
2 | * Samsung TV Mixer driver | |
3 | * | |
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | |
5 | * | |
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published | |
10 | * by the Free Software Foundation. either version 2 of the License, | |
11 | * or (at your option) any later version | |
12 | */ | |
13 | ||
f5c99037 SK |
14 | #define pr_fmt(fmt) "s5p-tv (mixer): " fmt |
15 | ||
fef1c8d0 TS |
16 | #include "mixer.h" |
17 | ||
18 | #include <media/v4l2-ioctl.h> | |
19 | #include <linux/videodev2.h> | |
20 | #include <linux/mm.h> | |
fc8ac777 | 21 | #include <linux/module.h> |
fef1c8d0 TS |
22 | #include <linux/version.h> |
23 | #include <linux/timer.h> | |
24 | #include <media/videobuf2-dma-contig.h> | |
25 | ||
26 | static int find_reg_callback(struct device *dev, void *p) | |
27 | { | |
28 | struct v4l2_subdev **sd = p; | |
29 | ||
30 | *sd = dev_get_drvdata(dev); | |
31 | /* non-zero value stops iteration */ | |
32 | return 1; | |
33 | } | |
34 | ||
35 | static struct v4l2_subdev *find_and_register_subdev( | |
36 | struct mxr_device *mdev, char *module_name) | |
37 | { | |
38 | struct device_driver *drv; | |
39 | struct v4l2_subdev *sd = NULL; | |
40 | int ret; | |
41 | ||
42 | /* TODO: add waiting until probe is finished */ | |
43 | drv = driver_find(module_name, &platform_bus_type); | |
44 | if (!drv) { | |
45 | mxr_warn(mdev, "module %s is missing\n", module_name); | |
46 | return NULL; | |
47 | } | |
48 | /* driver refcnt is increased, it is safe to iterate over devices */ | |
49 | ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback); | |
50 | /* ret == 0 means that find_reg_callback was never executed */ | |
51 | if (sd == NULL) { | |
52 | mxr_warn(mdev, "module %s provides no subdev!\n", module_name); | |
53 | goto done; | |
54 | } | |
55 | /* v4l2_device_register_subdev detects if sd is NULL */ | |
56 | ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd); | |
57 | if (ret) { | |
58 | mxr_warn(mdev, "failed to register subdev %s\n", sd->name); | |
59 | sd = NULL; | |
60 | } | |
61 | ||
62 | done: | |
fef1c8d0 TS |
63 | return sd; |
64 | } | |
65 | ||
66 | int __devinit mxr_acquire_video(struct mxr_device *mdev, | |
67 | struct mxr_output_conf *output_conf, int output_count) | |
68 | { | |
69 | struct device *dev = mdev->dev; | |
70 | struct v4l2_device *v4l2_dev = &mdev->v4l2_dev; | |
71 | int i; | |
72 | int ret = 0; | |
73 | struct v4l2_subdev *sd; | |
74 | ||
75 | strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name)); | |
76 | /* prepare context for V4L2 device */ | |
77 | ret = v4l2_device_register(dev, v4l2_dev); | |
78 | if (ret) { | |
79 | mxr_err(mdev, "could not register v4l2 device.\n"); | |
80 | goto fail; | |
81 | } | |
82 | ||
83 | mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); | |
84 | if (IS_ERR_OR_NULL(mdev->alloc_ctx)) { | |
85 | mxr_err(mdev, "could not acquire vb2 allocator\n"); | |
86 | goto fail_v4l2_dev; | |
87 | } | |
88 | ||
89 | /* registering outputs */ | |
90 | mdev->output_cnt = 0; | |
91 | for (i = 0; i < output_count; ++i) { | |
92 | struct mxr_output_conf *conf = &output_conf[i]; | |
93 | struct mxr_output *out; | |
94 | ||
95 | sd = find_and_register_subdev(mdev, conf->module_name); | |
96 | /* trying to register next output */ | |
97 | if (sd == NULL) | |
98 | continue; | |
99 | out = kzalloc(sizeof *out, GFP_KERNEL); | |
100 | if (out == NULL) { | |
101 | mxr_err(mdev, "no memory for '%s'\n", | |
102 | conf->output_name); | |
103 | ret = -ENOMEM; | |
104 | /* registered subdevs are removed in fail_v4l2_dev */ | |
105 | goto fail_output; | |
106 | } | |
107 | strlcpy(out->name, conf->output_name, sizeof(out->name)); | |
108 | out->sd = sd; | |
109 | out->cookie = conf->cookie; | |
110 | mdev->output[mdev->output_cnt++] = out; | |
111 | mxr_info(mdev, "added output '%s' from module '%s'\n", | |
112 | conf->output_name, conf->module_name); | |
113 | /* checking if maximal number of outputs is reached */ | |
114 | if (mdev->output_cnt >= MXR_MAX_OUTPUTS) | |
115 | break; | |
116 | } | |
117 | ||
118 | if (mdev->output_cnt == 0) { | |
119 | mxr_err(mdev, "failed to register any output\n"); | |
120 | ret = -ENODEV; | |
121 | /* skipping fail_output because there is nothing to free */ | |
122 | goto fail_vb2_allocator; | |
123 | } | |
124 | ||
125 | return 0; | |
126 | ||
127 | fail_output: | |
128 | /* kfree is NULL-safe */ | |
129 | for (i = 0; i < mdev->output_cnt; ++i) | |
130 | kfree(mdev->output[i]); | |
131 | memset(mdev->output, 0, sizeof mdev->output); | |
132 | ||
133 | fail_vb2_allocator: | |
134 | /* freeing allocator context */ | |
135 | vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); | |
136 | ||
137 | fail_v4l2_dev: | |
138 | /* NOTE: automatically unregister all subdevs */ | |
139 | v4l2_device_unregister(v4l2_dev); | |
140 | ||
141 | fail: | |
142 | return ret; | |
143 | } | |
144 | ||
e6364a98 | 145 | void mxr_release_video(struct mxr_device *mdev) |
fef1c8d0 TS |
146 | { |
147 | int i; | |
148 | ||
149 | /* kfree is NULL-safe */ | |
150 | for (i = 0; i < mdev->output_cnt; ++i) | |
151 | kfree(mdev->output[i]); | |
152 | ||
153 | vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); | |
154 | v4l2_device_unregister(&mdev->v4l2_dev); | |
155 | } | |
156 | ||
157 | static int mxr_querycap(struct file *file, void *priv, | |
158 | struct v4l2_capability *cap) | |
159 | { | |
160 | struct mxr_layer *layer = video_drvdata(file); | |
161 | ||
162 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
163 | ||
164 | strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); | |
165 | strlcpy(cap->card, layer->vfd.name, sizeof cap->card); | |
166 | sprintf(cap->bus_info, "%d", layer->idx); | |
167 | cap->version = KERNEL_VERSION(0, 1, 0); | |
168 | cap->capabilities = V4L2_CAP_STREAMING | | |
169 | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE; | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
0d066d3f | 174 | static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) |
fef1c8d0 | 175 | { |
0d066d3f TS |
176 | mxr_dbg(mdev, "src.full_size = (%u, %u)\n", |
177 | geo->src.full_width, geo->src.full_height); | |
178 | mxr_dbg(mdev, "src.size = (%u, %u)\n", | |
179 | geo->src.width, geo->src.height); | |
180 | mxr_dbg(mdev, "src.offset = (%u, %u)\n", | |
181 | geo->src.x_offset, geo->src.y_offset); | |
182 | mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", | |
183 | geo->dst.full_width, geo->dst.full_height); | |
184 | mxr_dbg(mdev, "dst.size = (%u, %u)\n", | |
185 | geo->dst.width, geo->dst.height); | |
186 | mxr_dbg(mdev, "dst.offset = (%u, %u)\n", | |
187 | geo->dst.x_offset, geo->dst.y_offset); | |
188 | mxr_dbg(mdev, "ratio = (%u, %u)\n", | |
189 | geo->x_ratio, geo->y_ratio); | |
fef1c8d0 TS |
190 | } |
191 | ||
192 | static void mxr_layer_default_geo(struct mxr_layer *layer) | |
193 | { | |
194 | struct mxr_device *mdev = layer->mdev; | |
195 | struct v4l2_mbus_framefmt mbus_fmt; | |
196 | ||
197 | memset(&layer->geo, 0, sizeof layer->geo); | |
198 | ||
199 | mxr_get_mbus_fmt(mdev, &mbus_fmt); | |
200 | ||
201 | layer->geo.dst.full_width = mbus_fmt.width; | |
202 | layer->geo.dst.full_height = mbus_fmt.height; | |
203 | layer->geo.dst.width = layer->geo.dst.full_width; | |
204 | layer->geo.dst.height = layer->geo.dst.full_height; | |
205 | layer->geo.dst.field = mbus_fmt.field; | |
206 | ||
207 | layer->geo.src.full_width = mbus_fmt.width; | |
208 | layer->geo.src.full_height = mbus_fmt.height; | |
209 | layer->geo.src.width = layer->geo.src.full_width; | |
210 | layer->geo.src.height = layer->geo.src.full_height; | |
211 | ||
0d066d3f TS |
212 | mxr_geometry_dump(mdev, &layer->geo); |
213 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); | |
214 | mxr_geometry_dump(mdev, &layer->geo); | |
fef1c8d0 TS |
215 | } |
216 | ||
0d066d3f | 217 | static void mxr_layer_update_output(struct mxr_layer *layer) |
fef1c8d0 | 218 | { |
0d066d3f TS |
219 | struct mxr_device *mdev = layer->mdev; |
220 | struct v4l2_mbus_framefmt mbus_fmt; | |
221 | ||
222 | mxr_get_mbus_fmt(mdev, &mbus_fmt); | |
223 | /* checking if update is needed */ | |
224 | if (layer->geo.dst.full_width == mbus_fmt.width && | |
225 | layer->geo.dst.full_height == mbus_fmt.width) | |
226 | return; | |
fef1c8d0 | 227 | |
0d066d3f TS |
228 | layer->geo.dst.full_width = mbus_fmt.width; |
229 | layer->geo.dst.full_height = mbus_fmt.height; | |
230 | layer->geo.dst.field = mbus_fmt.field; | |
231 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); | |
232 | ||
233 | mxr_geometry_dump(mdev, &layer->geo); | |
234 | } | |
fef1c8d0 TS |
235 | |
236 | static const struct mxr_format *find_format_by_fourcc( | |
237 | struct mxr_layer *layer, unsigned long fourcc); | |
238 | static const struct mxr_format *find_format_by_index( | |
239 | struct mxr_layer *layer, unsigned long index); | |
240 | ||
241 | static int mxr_enum_fmt(struct file *file, void *priv, | |
242 | struct v4l2_fmtdesc *f) | |
243 | { | |
244 | struct mxr_layer *layer = video_drvdata(file); | |
245 | struct mxr_device *mdev = layer->mdev; | |
246 | const struct mxr_format *fmt; | |
247 | ||
248 | mxr_dbg(mdev, "%s\n", __func__); | |
249 | fmt = find_format_by_index(layer, f->index); | |
250 | if (fmt == NULL) | |
251 | return -EINVAL; | |
252 | ||
253 | strlcpy(f->description, fmt->name, sizeof(f->description)); | |
254 | f->pixelformat = fmt->fourcc; | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
fef1c8d0 TS |
259 | static unsigned int divup(unsigned int divident, unsigned int divisor) |
260 | { | |
261 | return (divident + divisor - 1) / divisor; | |
262 | } | |
263 | ||
264 | unsigned long mxr_get_plane_size(const struct mxr_block *blk, | |
265 | unsigned int width, unsigned int height) | |
266 | { | |
267 | unsigned int bl_width = divup(width, blk->width); | |
268 | unsigned int bl_height = divup(height, blk->height); | |
269 | ||
270 | return bl_width * bl_height * blk->size; | |
271 | } | |
272 | ||
273 | static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, | |
274 | const struct mxr_format *fmt, u32 width, u32 height) | |
275 | { | |
276 | int i; | |
277 | ||
0d066d3f TS |
278 | /* checking if nothing to fill */ |
279 | if (!planes) | |
280 | return; | |
281 | ||
fef1c8d0 TS |
282 | memset(planes, 0, sizeof(*planes) * fmt->num_subframes); |
283 | for (i = 0; i < fmt->num_planes; ++i) { | |
284 | struct v4l2_plane_pix_format *plane = planes | |
285 | + fmt->plane2subframe[i]; | |
286 | const struct mxr_block *blk = &fmt->plane[i]; | |
287 | u32 bl_width = divup(width, blk->width); | |
288 | u32 bl_height = divup(height, blk->height); | |
289 | u32 sizeimage = bl_width * bl_height * blk->size; | |
290 | u16 bytesperline = bl_width * blk->size / blk->height; | |
291 | ||
292 | plane->sizeimage += sizeimage; | |
293 | plane->bytesperline = max(plane->bytesperline, bytesperline); | |
294 | } | |
295 | } | |
296 | ||
297 | static int mxr_g_fmt(struct file *file, void *priv, | |
298 | struct v4l2_format *f) | |
299 | { | |
300 | struct mxr_layer *layer = video_drvdata(file); | |
301 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | |
302 | ||
303 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
304 | ||
305 | pix->width = layer->geo.src.full_width; | |
306 | pix->height = layer->geo.src.full_height; | |
307 | pix->field = V4L2_FIELD_NONE; | |
308 | pix->pixelformat = layer->fmt->fourcc; | |
309 | pix->colorspace = layer->fmt->colorspace; | |
310 | mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
0d066d3f TS |
315 | static int mxr_s_fmt(struct file *file, void *priv, |
316 | struct v4l2_format *f) | |
fef1c8d0 TS |
317 | { |
318 | struct mxr_layer *layer = video_drvdata(file); | |
0d066d3f TS |
319 | const struct mxr_format *fmt; |
320 | struct v4l2_pix_format_mplane *pix; | |
321 | struct mxr_device *mdev = layer->mdev; | |
322 | struct mxr_geometry *geo = &layer->geo; | |
fef1c8d0 | 323 | |
0d066d3f TS |
324 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); |
325 | ||
326 | pix = &f->fmt.pix_mp; | |
327 | fmt = find_format_by_fourcc(layer, pix->pixelformat); | |
328 | if (fmt == NULL) { | |
329 | mxr_warn(mdev, "not recognized fourcc: %08x\n", | |
330 | pix->pixelformat); | |
fef1c8d0 | 331 | return -EINVAL; |
0d066d3f TS |
332 | } |
333 | layer->fmt = fmt; | |
334 | /* set source size to highest accepted value */ | |
335 | geo->src.full_width = max(geo->dst.full_width, pix->width); | |
336 | geo->src.full_height = max(geo->dst.full_height, pix->height); | |
337 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); | |
338 | mxr_geometry_dump(mdev, &layer->geo); | |
339 | /* set cropping to total visible screen */ | |
340 | geo->src.width = pix->width; | |
341 | geo->src.height = pix->height; | |
342 | geo->src.x_offset = 0; | |
343 | geo->src.y_offset = 0; | |
344 | /* assure consistency of geometry */ | |
345 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); | |
346 | mxr_geometry_dump(mdev, &layer->geo); | |
347 | /* set full size to lowest possible value */ | |
348 | geo->src.full_width = 0; | |
349 | geo->src.full_height = 0; | |
350 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); | |
351 | mxr_geometry_dump(mdev, &layer->geo); | |
352 | ||
353 | /* returning results */ | |
354 | mxr_g_fmt(file, priv, f); | |
355 | ||
fef1c8d0 TS |
356 | return 0; |
357 | } | |
358 | ||
0d066d3f TS |
359 | static int mxr_g_selection(struct file *file, void *fh, |
360 | struct v4l2_selection *s) | |
fef1c8d0 TS |
361 | { |
362 | struct mxr_layer *layer = video_drvdata(file); | |
0d066d3f | 363 | struct mxr_geometry *geo = &layer->geo; |
fef1c8d0 TS |
364 | |
365 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
0d066d3f TS |
366 | |
367 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && | |
368 | s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
369 | return -EINVAL; | |
370 | ||
371 | switch (s->target) { | |
c1334823 | 372 | case V4L2_SEL_TGT_CROP: |
0d066d3f TS |
373 | s->r.left = geo->src.x_offset; |
374 | s->r.top = geo->src.y_offset; | |
375 | s->r.width = geo->src.width; | |
376 | s->r.height = geo->src.height; | |
377 | break; | |
378 | case V4L2_SEL_TGT_CROP_DEFAULT: | |
379 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
380 | s->r.left = 0; | |
381 | s->r.top = 0; | |
382 | s->r.width = geo->src.full_width; | |
383 | s->r.height = geo->src.full_height; | |
384 | break; | |
c1334823 | 385 | case V4L2_SEL_TGT_COMPOSE: |
0d066d3f TS |
386 | case V4L2_SEL_TGT_COMPOSE_PADDED: |
387 | s->r.left = geo->dst.x_offset; | |
388 | s->r.top = geo->dst.y_offset; | |
389 | s->r.width = geo->dst.width; | |
390 | s->r.height = geo->dst.height; | |
391 | break; | |
392 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | |
393 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | |
394 | s->r.left = 0; | |
395 | s->r.top = 0; | |
396 | s->r.width = geo->dst.full_width; | |
397 | s->r.height = geo->dst.full_height; | |
398 | break; | |
399 | default: | |
fef1c8d0 | 400 | return -EINVAL; |
0d066d3f TS |
401 | } |
402 | ||
fef1c8d0 TS |
403 | return 0; |
404 | } | |
405 | ||
0d066d3f TS |
406 | /* returns 1 if rectangle 'a' is inside 'b' */ |
407 | static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b) | |
408 | { | |
409 | if (a->left < b->left) | |
410 | return 0; | |
411 | if (a->top < b->top) | |
412 | return 0; | |
413 | if (a->left + a->width > b->left + b->width) | |
414 | return 0; | |
415 | if (a->top + a->height > b->top + b->height) | |
416 | return 0; | |
417 | return 1; | |
418 | } | |
419 | ||
420 | static int mxr_s_selection(struct file *file, void *fh, | |
421 | struct v4l2_selection *s) | |
fef1c8d0 TS |
422 | { |
423 | struct mxr_layer *layer = video_drvdata(file); | |
0d066d3f TS |
424 | struct mxr_geometry *geo = &layer->geo; |
425 | struct mxr_crop *target = NULL; | |
426 | enum mxr_geometry_stage stage; | |
427 | struct mxr_geometry tmp; | |
428 | struct v4l2_rect res; | |
fef1c8d0 | 429 | |
0d066d3f TS |
430 | memset(&res, 0, sizeof res); |
431 | ||
432 | mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, | |
433 | s->r.width, s->r.height, s->r.left, s->r.top); | |
434 | ||
435 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && | |
436 | s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | |
fef1c8d0 | 437 | return -EINVAL; |
0d066d3f TS |
438 | |
439 | switch (s->target) { | |
440 | /* ignore read-only targets */ | |
441 | case V4L2_SEL_TGT_CROP_DEFAULT: | |
442 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
443 | res.width = geo->src.full_width; | |
444 | res.height = geo->src.full_height; | |
445 | break; | |
446 | ||
447 | /* ignore read-only targets */ | |
448 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | |
449 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | |
450 | res.width = geo->dst.full_width; | |
451 | res.height = geo->dst.full_height; | |
452 | break; | |
453 | ||
c1334823 | 454 | case V4L2_SEL_TGT_CROP: |
0d066d3f TS |
455 | target = &geo->src; |
456 | stage = MXR_GEOMETRY_CROP; | |
457 | break; | |
c1334823 | 458 | case V4L2_SEL_TGT_COMPOSE: |
0d066d3f TS |
459 | case V4L2_SEL_TGT_COMPOSE_PADDED: |
460 | target = &geo->dst; | |
461 | stage = MXR_GEOMETRY_COMPOSE; | |
462 | break; | |
463 | default: | |
464 | return -EINVAL; | |
465 | } | |
466 | /* apply change and update geometry if needed */ | |
467 | if (target) { | |
468 | /* backup current geometry if setup fails */ | |
469 | memcpy(&tmp, geo, sizeof tmp); | |
470 | ||
471 | /* apply requested selection */ | |
472 | target->x_offset = s->r.left; | |
473 | target->y_offset = s->r.top; | |
474 | target->width = s->r.width; | |
475 | target->height = s->r.height; | |
476 | ||
477 | layer->ops.fix_geometry(layer, stage, s->flags); | |
478 | ||
479 | /* retrieve update selection rectangle */ | |
480 | res.left = target->x_offset; | |
481 | res.top = target->y_offset; | |
482 | res.width = target->width; | |
483 | res.height = target->height; | |
484 | ||
485 | mxr_geometry_dump(layer->mdev, &layer->geo); | |
486 | } | |
487 | ||
488 | /* checking if the rectangle satisfies constraints */ | |
489 | if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r)) | |
490 | goto fail; | |
491 | if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res)) | |
492 | goto fail; | |
493 | ||
494 | /* return result rectangle */ | |
495 | s->r = res; | |
496 | ||
fef1c8d0 | 497 | return 0; |
0d066d3f TS |
498 | fail: |
499 | /* restore old geometry, which is not touched if target is NULL */ | |
500 | if (target) | |
501 | memcpy(geo, &tmp, sizeof tmp); | |
502 | return -ERANGE; | |
fef1c8d0 TS |
503 | } |
504 | ||
505 | static int mxr_enum_dv_presets(struct file *file, void *fh, | |
506 | struct v4l2_dv_enum_preset *preset) | |
507 | { | |
508 | struct mxr_layer *layer = video_drvdata(file); | |
509 | struct mxr_device *mdev = layer->mdev; | |
510 | int ret; | |
511 | ||
512 | /* lock protects from changing sd_out */ | |
513 | mutex_lock(&mdev->mutex); | |
514 | ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset); | |
515 | mutex_unlock(&mdev->mutex); | |
516 | ||
517 | return ret ? -EINVAL : 0; | |
518 | } | |
519 | ||
520 | static int mxr_s_dv_preset(struct file *file, void *fh, | |
521 | struct v4l2_dv_preset *preset) | |
522 | { | |
523 | struct mxr_layer *layer = video_drvdata(file); | |
524 | struct mxr_device *mdev = layer->mdev; | |
525 | int ret; | |
526 | ||
527 | /* lock protects from changing sd_out */ | |
528 | mutex_lock(&mdev->mutex); | |
529 | ||
530 | /* preset change cannot be done while there is an entity | |
531 | * dependant on output configuration | |
532 | */ | |
533 | if (mdev->n_output > 0) { | |
534 | mutex_unlock(&mdev->mutex); | |
535 | return -EBUSY; | |
536 | } | |
537 | ||
538 | ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset); | |
539 | ||
540 | mutex_unlock(&mdev->mutex); | |
541 | ||
0d066d3f TS |
542 | mxr_layer_update_output(layer); |
543 | ||
fef1c8d0 TS |
544 | /* any failure should return EINVAL according to V4L2 doc */ |
545 | return ret ? -EINVAL : 0; | |
546 | } | |
547 | ||
548 | static int mxr_g_dv_preset(struct file *file, void *fh, | |
549 | struct v4l2_dv_preset *preset) | |
550 | { | |
551 | struct mxr_layer *layer = video_drvdata(file); | |
552 | struct mxr_device *mdev = layer->mdev; | |
553 | int ret; | |
554 | ||
555 | /* lock protects from changing sd_out */ | |
556 | mutex_lock(&mdev->mutex); | |
557 | ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset); | |
558 | mutex_unlock(&mdev->mutex); | |
559 | ||
560 | return ret ? -EINVAL : 0; | |
561 | } | |
562 | ||
563 | static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) | |
564 | { | |
565 | struct mxr_layer *layer = video_drvdata(file); | |
566 | struct mxr_device *mdev = layer->mdev; | |
567 | int ret; | |
568 | ||
569 | /* lock protects from changing sd_out */ | |
570 | mutex_lock(&mdev->mutex); | |
571 | ||
572 | /* standard change cannot be done while there is an entity | |
573 | * dependant on output configuration | |
574 | */ | |
575 | if (mdev->n_output > 0) { | |
576 | mutex_unlock(&mdev->mutex); | |
577 | return -EBUSY; | |
578 | } | |
579 | ||
580 | ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm); | |
581 | ||
582 | mutex_unlock(&mdev->mutex); | |
583 | ||
0d066d3f TS |
584 | mxr_layer_update_output(layer); |
585 | ||
fef1c8d0 TS |
586 | return ret ? -EINVAL : 0; |
587 | } | |
588 | ||
589 | static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm) | |
590 | { | |
591 | struct mxr_layer *layer = video_drvdata(file); | |
592 | struct mxr_device *mdev = layer->mdev; | |
593 | int ret; | |
594 | ||
595 | /* lock protects from changing sd_out */ | |
596 | mutex_lock(&mdev->mutex); | |
597 | ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm); | |
598 | mutex_unlock(&mdev->mutex); | |
599 | ||
600 | return ret ? -EINVAL : 0; | |
601 | } | |
602 | ||
603 | static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) | |
604 | { | |
605 | struct mxr_layer *layer = video_drvdata(file); | |
606 | struct mxr_device *mdev = layer->mdev; | |
607 | struct mxr_output *out; | |
608 | struct v4l2_subdev *sd; | |
609 | ||
610 | if (a->index >= mdev->output_cnt) | |
611 | return -EINVAL; | |
612 | out = mdev->output[a->index]; | |
613 | BUG_ON(out == NULL); | |
614 | sd = out->sd; | |
615 | strlcpy(a->name, out->name, sizeof(a->name)); | |
616 | ||
617 | /* try to obtain supported tv norms */ | |
618 | v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); | |
619 | a->capabilities = 0; | |
620 | if (sd->ops->video && sd->ops->video->s_dv_preset) | |
621 | a->capabilities |= V4L2_OUT_CAP_PRESETS; | |
622 | if (sd->ops->video && sd->ops->video->s_std_output) | |
623 | a->capabilities |= V4L2_OUT_CAP_STD; | |
624 | a->type = V4L2_OUTPUT_TYPE_ANALOG; | |
625 | ||
626 | return 0; | |
627 | } | |
628 | ||
629 | static int mxr_s_output(struct file *file, void *fh, unsigned int i) | |
630 | { | |
631 | struct video_device *vfd = video_devdata(file); | |
632 | struct mxr_layer *layer = video_drvdata(file); | |
633 | struct mxr_device *mdev = layer->mdev; | |
fef1c8d0 TS |
634 | |
635 | if (i >= mdev->output_cnt || mdev->output[i] == NULL) | |
636 | return -EINVAL; | |
637 | ||
638 | mutex_lock(&mdev->mutex); | |
639 | if (mdev->n_output > 0) { | |
0d066d3f TS |
640 | mutex_unlock(&mdev->mutex); |
641 | return -EBUSY; | |
fef1c8d0 TS |
642 | } |
643 | mdev->current_output = i; | |
644 | vfd->tvnorms = 0; | |
645 | v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, | |
646 | &vfd->tvnorms); | |
0d066d3f TS |
647 | mutex_unlock(&mdev->mutex); |
648 | ||
649 | /* update layers geometry */ | |
650 | mxr_layer_update_output(layer); | |
651 | ||
fef1c8d0 TS |
652 | mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); |
653 | ||
0d066d3f | 654 | return 0; |
fef1c8d0 TS |
655 | } |
656 | ||
657 | static int mxr_g_output(struct file *file, void *fh, unsigned int *p) | |
658 | { | |
659 | struct mxr_layer *layer = video_drvdata(file); | |
660 | struct mxr_device *mdev = layer->mdev; | |
661 | ||
662 | mutex_lock(&mdev->mutex); | |
663 | *p = mdev->current_output; | |
664 | mutex_unlock(&mdev->mutex); | |
665 | ||
666 | return 0; | |
667 | } | |
668 | ||
669 | static int mxr_reqbufs(struct file *file, void *priv, | |
670 | struct v4l2_requestbuffers *p) | |
671 | { | |
672 | struct mxr_layer *layer = video_drvdata(file); | |
673 | ||
674 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
675 | return vb2_reqbufs(&layer->vb_queue, p); | |
676 | } | |
677 | ||
678 | static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) | |
679 | { | |
680 | struct mxr_layer *layer = video_drvdata(file); | |
681 | ||
682 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
683 | return vb2_querybuf(&layer->vb_queue, p); | |
684 | } | |
685 | ||
686 | static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) | |
687 | { | |
688 | struct mxr_layer *layer = video_drvdata(file); | |
689 | ||
690 | mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index); | |
691 | return vb2_qbuf(&layer->vb_queue, p); | |
692 | } | |
693 | ||
694 | static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) | |
695 | { | |
696 | struct mxr_layer *layer = video_drvdata(file); | |
697 | ||
698 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
699 | return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); | |
700 | } | |
701 | ||
702 | static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) | |
703 | { | |
704 | struct mxr_layer *layer = video_drvdata(file); | |
705 | ||
706 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
707 | return vb2_streamon(&layer->vb_queue, i); | |
708 | } | |
709 | ||
710 | static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) | |
711 | { | |
712 | struct mxr_layer *layer = video_drvdata(file); | |
713 | ||
714 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
715 | return vb2_streamoff(&layer->vb_queue, i); | |
716 | } | |
717 | ||
718 | static const struct v4l2_ioctl_ops mxr_ioctl_ops = { | |
719 | .vidioc_querycap = mxr_querycap, | |
720 | /* format handling */ | |
721 | .vidioc_enum_fmt_vid_out = mxr_enum_fmt, | |
722 | .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt, | |
723 | .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt, | |
724 | /* buffer control */ | |
725 | .vidioc_reqbufs = mxr_reqbufs, | |
726 | .vidioc_querybuf = mxr_querybuf, | |
727 | .vidioc_qbuf = mxr_qbuf, | |
728 | .vidioc_dqbuf = mxr_dqbuf, | |
729 | /* Streaming control */ | |
730 | .vidioc_streamon = mxr_streamon, | |
731 | .vidioc_streamoff = mxr_streamoff, | |
732 | /* Preset functions */ | |
733 | .vidioc_enum_dv_presets = mxr_enum_dv_presets, | |
734 | .vidioc_s_dv_preset = mxr_s_dv_preset, | |
735 | .vidioc_g_dv_preset = mxr_g_dv_preset, | |
736 | /* analog TV standard functions */ | |
737 | .vidioc_s_std = mxr_s_std, | |
738 | .vidioc_g_std = mxr_g_std, | |
739 | /* Output handling */ | |
740 | .vidioc_enum_output = mxr_enum_output, | |
741 | .vidioc_s_output = mxr_s_output, | |
742 | .vidioc_g_output = mxr_g_output, | |
0d066d3f TS |
743 | /* selection ioctls */ |
744 | .vidioc_g_selection = mxr_g_selection, | |
745 | .vidioc_s_selection = mxr_s_selection, | |
fef1c8d0 TS |
746 | }; |
747 | ||
748 | static int mxr_video_open(struct file *file) | |
749 | { | |
750 | struct mxr_layer *layer = video_drvdata(file); | |
751 | struct mxr_device *mdev = layer->mdev; | |
752 | int ret = 0; | |
753 | ||
754 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); | |
00ccdc3c HV |
755 | if (mutex_lock_interruptible(&layer->mutex)) |
756 | return -ERESTARTSYS; | |
fef1c8d0 TS |
757 | /* assure device probe is finished */ |
758 | wait_for_device_probe(); | |
759 | /* creating context for file descriptor */ | |
760 | ret = v4l2_fh_open(file); | |
761 | if (ret) { | |
762 | mxr_err(mdev, "v4l2_fh_open failed\n"); | |
00ccdc3c | 763 | goto unlock; |
fef1c8d0 TS |
764 | } |
765 | ||
766 | /* leaving if layer is already initialized */ | |
767 | if (!v4l2_fh_is_singular_file(file)) | |
00ccdc3c | 768 | goto unlock; |
fef1c8d0 TS |
769 | |
770 | /* FIXME: should power be enabled on open? */ | |
771 | ret = mxr_power_get(mdev); | |
772 | if (ret) { | |
773 | mxr_err(mdev, "power on failed\n"); | |
774 | goto fail_fh_open; | |
775 | } | |
776 | ||
777 | ret = vb2_queue_init(&layer->vb_queue); | |
778 | if (ret != 0) { | |
779 | mxr_err(mdev, "failed to initialize vb2 queue\n"); | |
780 | goto fail_power; | |
781 | } | |
782 | /* set default format, first on the list */ | |
783 | layer->fmt = layer->fmt_array[0]; | |
784 | /* setup default geometry */ | |
785 | mxr_layer_default_geo(layer); | |
00ccdc3c | 786 | mutex_unlock(&layer->mutex); |
fef1c8d0 TS |
787 | |
788 | return 0; | |
789 | ||
790 | fail_power: | |
791 | mxr_power_put(mdev); | |
792 | ||
793 | fail_fh_open: | |
794 | v4l2_fh_release(file); | |
795 | ||
00ccdc3c HV |
796 | unlock: |
797 | mutex_unlock(&layer->mutex); | |
798 | ||
fef1c8d0 TS |
799 | return ret; |
800 | } | |
801 | ||
802 | static unsigned int | |
803 | mxr_video_poll(struct file *file, struct poll_table_struct *wait) | |
804 | { | |
805 | struct mxr_layer *layer = video_drvdata(file); | |
00ccdc3c | 806 | unsigned int res; |
fef1c8d0 TS |
807 | |
808 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
809 | ||
00ccdc3c HV |
810 | mutex_lock(&layer->mutex); |
811 | res = vb2_poll(&layer->vb_queue, file, wait); | |
812 | mutex_unlock(&layer->mutex); | |
813 | return res; | |
fef1c8d0 TS |
814 | } |
815 | ||
816 | static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma) | |
817 | { | |
818 | struct mxr_layer *layer = video_drvdata(file); | |
00ccdc3c | 819 | int ret; |
fef1c8d0 TS |
820 | |
821 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
822 | ||
00ccdc3c HV |
823 | if (mutex_lock_interruptible(&layer->mutex)) |
824 | return -ERESTARTSYS; | |
825 | ret = vb2_mmap(&layer->vb_queue, vma); | |
826 | mutex_unlock(&layer->mutex); | |
827 | return ret; | |
fef1c8d0 TS |
828 | } |
829 | ||
830 | static int mxr_video_release(struct file *file) | |
831 | { | |
832 | struct mxr_layer *layer = video_drvdata(file); | |
833 | ||
834 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | |
00ccdc3c | 835 | mutex_lock(&layer->mutex); |
fef1c8d0 TS |
836 | if (v4l2_fh_is_singular_file(file)) { |
837 | vb2_queue_release(&layer->vb_queue); | |
838 | mxr_power_put(layer->mdev); | |
839 | } | |
840 | v4l2_fh_release(file); | |
00ccdc3c | 841 | mutex_unlock(&layer->mutex); |
fef1c8d0 TS |
842 | return 0; |
843 | } | |
844 | ||
845 | static const struct v4l2_file_operations mxr_fops = { | |
846 | .owner = THIS_MODULE, | |
847 | .open = mxr_video_open, | |
848 | .poll = mxr_video_poll, | |
849 | .mmap = mxr_video_mmap, | |
850 | .release = mxr_video_release, | |
851 | .unlocked_ioctl = video_ioctl2, | |
852 | }; | |
853 | ||
fc714e70 GL |
854 | static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, |
855 | unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], | |
fef1c8d0 TS |
856 | void *alloc_ctxs[]) |
857 | { | |
858 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | |
859 | const struct mxr_format *fmt = layer->fmt; | |
860 | int i; | |
861 | struct mxr_device *mdev = layer->mdev; | |
862 | struct v4l2_plane_pix_format planes[3]; | |
863 | ||
864 | mxr_dbg(mdev, "%s\n", __func__); | |
865 | /* checking if format was configured */ | |
866 | if (fmt == NULL) | |
867 | return -EINVAL; | |
868 | mxr_dbg(mdev, "fmt = %s\n", fmt->name); | |
869 | mxr_mplane_fill(planes, fmt, layer->geo.src.full_width, | |
870 | layer->geo.src.full_height); | |
871 | ||
872 | *nplanes = fmt->num_subframes; | |
873 | for (i = 0; i < fmt->num_subframes; ++i) { | |
874 | alloc_ctxs[i] = layer->mdev->alloc_ctx; | |
c1bf9c65 | 875 | sizes[i] = planes[i].sizeimage; |
c31e3c4b | 876 | mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); |
fef1c8d0 TS |
877 | } |
878 | ||
879 | if (*nbuffers == 0) | |
880 | *nbuffers = 1; | |
881 | ||
882 | return 0; | |
883 | } | |
884 | ||
885 | static void buf_queue(struct vb2_buffer *vb) | |
886 | { | |
887 | struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb); | |
888 | struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); | |
889 | struct mxr_device *mdev = layer->mdev; | |
890 | unsigned long flags; | |
fef1c8d0 TS |
891 | |
892 | spin_lock_irqsave(&layer->enq_slock, flags); | |
fef1c8d0 TS |
893 | list_add_tail(&buffer->list, &layer->enq_list); |
894 | spin_unlock_irqrestore(&layer->enq_slock, flags); | |
fef1c8d0 TS |
895 | |
896 | mxr_dbg(mdev, "queuing buffer\n"); | |
897 | } | |
898 | ||
899 | static void wait_lock(struct vb2_queue *vq) | |
900 | { | |
901 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | |
902 | ||
903 | mxr_dbg(layer->mdev, "%s\n", __func__); | |
904 | mutex_lock(&layer->mutex); | |
905 | } | |
906 | ||
907 | static void wait_unlock(struct vb2_queue *vq) | |
908 | { | |
909 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | |
910 | ||
911 | mxr_dbg(layer->mdev, "%s\n", __func__); | |
912 | mutex_unlock(&layer->mutex); | |
913 | } | |
914 | ||
bd323e28 | 915 | static int start_streaming(struct vb2_queue *vq, unsigned int count) |
fef1c8d0 TS |
916 | { |
917 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | |
918 | struct mxr_device *mdev = layer->mdev; | |
919 | unsigned long flags; | |
920 | ||
921 | mxr_dbg(mdev, "%s\n", __func__); | |
bd323e28 MS |
922 | |
923 | if (count == 0) { | |
924 | mxr_dbg(mdev, "no output buffers queued\n"); | |
925 | return -EINVAL; | |
926 | } | |
927 | ||
fef1c8d0 TS |
928 | /* block any changes in output configuration */ |
929 | mxr_output_get(mdev); | |
930 | ||
0d066d3f | 931 | mxr_layer_update_output(layer); |
fef1c8d0 TS |
932 | layer->ops.format_set(layer); |
933 | /* enabling layer in hardware */ | |
934 | spin_lock_irqsave(&layer->enq_slock, flags); | |
bd323e28 | 935 | layer->state = MXR_LAYER_STREAMING; |
fef1c8d0 TS |
936 | spin_unlock_irqrestore(&layer->enq_slock, flags); |
937 | ||
bd323e28 MS |
938 | layer->ops.stream_set(layer, MXR_ENABLE); |
939 | mxr_streamer_get(mdev); | |
940 | ||
fef1c8d0 TS |
941 | return 0; |
942 | } | |
943 | ||
944 | static void mxr_watchdog(unsigned long arg) | |
945 | { | |
946 | struct mxr_layer *layer = (struct mxr_layer *) arg; | |
947 | struct mxr_device *mdev = layer->mdev; | |
948 | unsigned long flags; | |
949 | ||
950 | mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name); | |
951 | ||
952 | spin_lock_irqsave(&layer->enq_slock, flags); | |
953 | ||
954 | if (layer->update_buf == layer->shadow_buf) | |
955 | layer->update_buf = NULL; | |
956 | if (layer->update_buf) { | |
957 | vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR); | |
958 | layer->update_buf = NULL; | |
959 | } | |
960 | if (layer->shadow_buf) { | |
961 | vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR); | |
962 | layer->shadow_buf = NULL; | |
963 | } | |
964 | spin_unlock_irqrestore(&layer->enq_slock, flags); | |
965 | } | |
966 | ||
967 | static int stop_streaming(struct vb2_queue *vq) | |
968 | { | |
969 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | |
970 | struct mxr_device *mdev = layer->mdev; | |
971 | unsigned long flags; | |
972 | struct timer_list watchdog; | |
973 | struct mxr_buffer *buf, *buf_tmp; | |
974 | ||
975 | mxr_dbg(mdev, "%s\n", __func__); | |
976 | ||
977 | spin_lock_irqsave(&layer->enq_slock, flags); | |
978 | ||
979 | /* reset list */ | |
980 | layer->state = MXR_LAYER_STREAMING_FINISH; | |
981 | ||
982 | /* set all buffer to be done */ | |
983 | list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) { | |
984 | list_del(&buf->list); | |
985 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | |
986 | } | |
987 | ||
988 | spin_unlock_irqrestore(&layer->enq_slock, flags); | |
989 | ||
990 | /* give 1 seconds to complete to complete last buffers */ | |
991 | setup_timer_on_stack(&watchdog, mxr_watchdog, | |
992 | (unsigned long)layer); | |
993 | mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000)); | |
994 | ||
995 | /* wait until all buffers are goes to done state */ | |
996 | vb2_wait_for_all_buffers(vq); | |
997 | ||
998 | /* stop timer if all synchronization is done */ | |
999 | del_timer_sync(&watchdog); | |
1000 | destroy_timer_on_stack(&watchdog); | |
1001 | ||
1002 | /* stopping hardware */ | |
1003 | spin_lock_irqsave(&layer->enq_slock, flags); | |
1004 | layer->state = MXR_LAYER_IDLE; | |
1005 | spin_unlock_irqrestore(&layer->enq_slock, flags); | |
1006 | ||
1007 | /* disabling layer in hardware */ | |
1008 | layer->ops.stream_set(layer, MXR_DISABLE); | |
1009 | /* remove one streamer */ | |
1010 | mxr_streamer_put(mdev); | |
1011 | /* allow changes in output configuration */ | |
1012 | mxr_output_put(mdev); | |
1013 | return 0; | |
1014 | } | |
1015 | ||
1016 | static struct vb2_ops mxr_video_qops = { | |
1017 | .queue_setup = queue_setup, | |
1018 | .buf_queue = buf_queue, | |
1019 | .wait_prepare = wait_unlock, | |
1020 | .wait_finish = wait_lock, | |
1021 | .start_streaming = start_streaming, | |
1022 | .stop_streaming = stop_streaming, | |
1023 | }; | |
1024 | ||
1025 | /* FIXME: try to put this functions to mxr_base_layer_create */ | |
1026 | int mxr_base_layer_register(struct mxr_layer *layer) | |
1027 | { | |
1028 | struct mxr_device *mdev = layer->mdev; | |
1029 | int ret; | |
1030 | ||
1031 | ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1); | |
1032 | if (ret) | |
1033 | mxr_err(mdev, "failed to register video device\n"); | |
1034 | else | |
1035 | mxr_info(mdev, "registered layer %s as /dev/video%d\n", | |
1036 | layer->vfd.name, layer->vfd.num); | |
1037 | return ret; | |
1038 | } | |
1039 | ||
1040 | void mxr_base_layer_unregister(struct mxr_layer *layer) | |
1041 | { | |
1042 | video_unregister_device(&layer->vfd); | |
1043 | } | |
1044 | ||
1045 | void mxr_layer_release(struct mxr_layer *layer) | |
1046 | { | |
1047 | if (layer->ops.release) | |
1048 | layer->ops.release(layer); | |
1049 | } | |
1050 | ||
1051 | void mxr_base_layer_release(struct mxr_layer *layer) | |
1052 | { | |
1053 | kfree(layer); | |
1054 | } | |
1055 | ||
1056 | static void mxr_vfd_release(struct video_device *vdev) | |
1057 | { | |
f5c99037 | 1058 | pr_info("video device release\n"); |
fef1c8d0 TS |
1059 | } |
1060 | ||
1061 | struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, | |
1062 | int idx, char *name, struct mxr_layer_ops *ops) | |
1063 | { | |
1064 | struct mxr_layer *layer; | |
1065 | ||
1066 | layer = kzalloc(sizeof *layer, GFP_KERNEL); | |
1067 | if (layer == NULL) { | |
1068 | mxr_err(mdev, "not enough memory for layer.\n"); | |
1069 | goto fail; | |
1070 | } | |
1071 | ||
1072 | layer->mdev = mdev; | |
1073 | layer->idx = idx; | |
1074 | layer->ops = *ops; | |
1075 | ||
1076 | spin_lock_init(&layer->enq_slock); | |
1077 | INIT_LIST_HEAD(&layer->enq_list); | |
1078 | mutex_init(&layer->mutex); | |
1079 | ||
1080 | layer->vfd = (struct video_device) { | |
1081 | .minor = -1, | |
1082 | .release = mxr_vfd_release, | |
1083 | .fops = &mxr_fops, | |
954f340f | 1084 | .vfl_dir = VFL_DIR_TX, |
fef1c8d0 TS |
1085 | .ioctl_ops = &mxr_ioctl_ops, |
1086 | }; | |
1087 | strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); | |
1088 | /* let framework control PRIORITY */ | |
1089 | set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); | |
1090 | ||
1091 | video_set_drvdata(&layer->vfd, layer); | |
1092 | layer->vfd.lock = &layer->mutex; | |
1093 | layer->vfd.v4l2_dev = &mdev->v4l2_dev; | |
1094 | ||
1095 | layer->vb_queue = (struct vb2_queue) { | |
1096 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | |
1097 | .io_modes = VB2_MMAP | VB2_USERPTR, | |
1098 | .drv_priv = layer, | |
1099 | .buf_struct_size = sizeof(struct mxr_buffer), | |
1100 | .ops = &mxr_video_qops, | |
1101 | .mem_ops = &vb2_dma_contig_memops, | |
1102 | }; | |
1103 | ||
1104 | return layer; | |
1105 | ||
1106 | fail: | |
1107 | return NULL; | |
1108 | } | |
1109 | ||
1110 | static const struct mxr_format *find_format_by_fourcc( | |
1111 | struct mxr_layer *layer, unsigned long fourcc) | |
1112 | { | |
1113 | int i; | |
1114 | ||
1115 | for (i = 0; i < layer->fmt_array_size; ++i) | |
1116 | if (layer->fmt_array[i]->fourcc == fourcc) | |
1117 | return layer->fmt_array[i]; | |
1118 | return NULL; | |
1119 | } | |
1120 | ||
1121 | static const struct mxr_format *find_format_by_index( | |
1122 | struct mxr_layer *layer, unsigned long index) | |
1123 | { | |
1124 | if (index >= layer->fmt_array_size) | |
1125 | return NULL; | |
1126 | return layer->fmt_array[index]; | |
1127 | } | |
1128 |