FILE
imx8-isi-m2m.c
File preview with syntax highlighting.
linux-6.19.14/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform 4 * 5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which 6 * used to process image from camera sensor or memory to memory or DC 7 * 8 * Copyright (c) 2019 NXP Semiconductor 9 */ 10 11#include <linux/container_of.h> 12#include <linux/device.h> 13#include <linux/errno.h> 14#include <linux/kernel.h> 15#include <linux/limits.h> 16#include <linux/minmax.h> 17#include <linux/mutex.h> 18#include <linux/pm_runtime.h> 19#include <linux/slab.h> 20#include <linux/spinlock.h> 21#include <linux/string.h> 22#include <linux/types.h> 23#include <linux/videodev2.h> 24 25#include <media/media-entity.h> 26#include <media/v4l2-ctrls.h> 27#include <media/v4l2-device.h> 28#include <media/v4l2-event.h> 29#include <media/v4l2-fh.h> 30#include <media/v4l2-ioctl.h> 31#include <media/v4l2-mem2mem.h> 32#include <media/videobuf2-core.h> 33#include <media/videobuf2-dma-contig.h> 34 35#include "imx8-isi-core.h" 36 37struct mxc_isi_m2m_buffer { 38 struct v4l2_m2m_buffer buf; 39 dma_addr_t dma_addrs[3]; 40}; 41 42struct mxc_isi_m2m_ctx_queue_data { 43 struct v4l2_pix_format_mplane format; 44 const struct mxc_isi_format_info *info; 45 u32 sequence; 46}; 47 48struct mxc_isi_m2m_ctx { 49 struct v4l2_fh fh; 50 struct mxc_isi_m2m *m2m; 51 52 /* Protects the m2m vb2 queues */ 53 struct mutex vb2_lock; 54 55 struct { 56 struct mxc_isi_m2m_ctx_queue_data out; 57 struct mxc_isi_m2m_ctx_queue_data cap; 58 } queues; 59 60 struct { 61 struct v4l2_ctrl_handler handler; 62 unsigned int alpha; 63 bool hflip; 64 bool vflip; 65 } ctrls; 66 67 bool chained; 68}; 69 70static inline struct mxc_isi_m2m_buffer * 71to_isi_m2m_buffer(struct vb2_v4l2_buffer *buf) 72{ 73 return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb); 74} 75 76static inline struct mxc_isi_m2m_ctx *file_to_isi_m2m_ctx(struct file *filp) 77{ 78 return container_of(file_to_v4l2_fh(filp), struct mxc_isi_m2m_ctx, fh); 79} 80 81static inline struct mxc_isi_m2m_ctx_queue_data * 82mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx *ctx, enum v4l2_buf_type type) 83{ 84 if (V4L2_TYPE_IS_OUTPUT(type)) 85 return &ctx->queues.out; 86 else 87 return &ctx->queues.cap; 88} 89 90/* ----------------------------------------------------------------------------- 91 * V4L2 M2M device operations 92 */ 93 94static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status) 95{ 96 struct mxc_isi_m2m *m2m = &pipe->isi->m2m; 97 struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; 98 struct mxc_isi_m2m_ctx *ctx; 99 100 ctx = v4l2_m2m_get_curr_priv(m2m->m2m_dev); 101 if (!ctx) { 102 dev_err(m2m->isi->dev, 103 "Instance released before the end of transaction\n"); 104 return; 105 } 106 107 src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 108 dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 109 110 v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf); 111 112 src_vbuf->sequence = ctx->queues.out.sequence++; 113 dst_vbuf->sequence = ctx->queues.cap.sequence++; 114 115 v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_DONE); 116 v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_DONE); 117 118 v4l2_m2m_job_finish(m2m->m2m_dev, ctx->fh.m2m_ctx); 119} 120 121static void mxc_isi_m2m_device_run(void *priv) 122{ 123 struct mxc_isi_m2m_ctx *ctx = priv; 124 struct mxc_isi_m2m *m2m = ctx->m2m; 125 struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; 126 struct mxc_isi_m2m_buffer *src_buf, *dst_buf; 127 128 mxc_isi_channel_disable(m2m->pipe); 129 130 mutex_lock(&m2m->lock); 131 132 /* If the context has changed, reconfigure the channel. */ 133 if (m2m->last_ctx != ctx) { 134 const struct v4l2_area in_size = { 135 .width = ctx->queues.out.format.width, 136 .height = ctx->queues.out.format.height, 137 }; 138 const struct v4l2_area scale = { 139 .width = ctx->queues.cap.format.width, 140 .height = ctx->queues.cap.format.height, 141 }; 142 const struct v4l2_rect crop = { 143 .width = ctx->queues.cap.format.width, 144 .height = ctx->queues.cap.format.height, 145 }; 146 147 mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, 148 &in_size, &scale, &crop, 149 ctx->queues.out.info->encoding, 150 ctx->queues.cap.info->encoding); 151 mxc_isi_channel_set_input_format(m2m->pipe, 152 ctx->queues.out.info, 153 &ctx->queues.out.format); 154 mxc_isi_channel_set_output_format(m2m->pipe, 155 ctx->queues.cap.info, 156 &ctx->queues.cap.format); 157 158 m2m->last_ctx = ctx; 159 } 160 161 mutex_unlock(&m2m->lock); 162 163 mutex_lock(ctx->ctrls.handler.lock); 164 mxc_isi_channel_set_alpha(m2m->pipe, ctx->ctrls.alpha); 165 mxc_isi_channel_set_flip(m2m->pipe, ctx->ctrls.hflip, ctx->ctrls.vflip); 166 mutex_unlock(ctx->ctrls.handler.lock); 167 168 src_vbuf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 169 dst_vbuf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 170 171 src_buf = to_isi_m2m_buffer(src_vbuf); 172 dst_buf = to_isi_m2m_buffer(dst_vbuf); 173 174 mxc_isi_channel_set_inbuf(m2m->pipe, src_buf->dma_addrs[0]); 175 mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF1); 176 mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF2); 177 178 mxc_isi_channel_enable(m2m->pipe); 179 180 mxc_isi_channel_m2m_start(m2m->pipe); 181} 182 183static const struct v4l2_m2m_ops mxc_isi_m2m_ops = { 184 .device_run = mxc_isi_m2m_device_run, 185}; 186 187/* ----------------------------------------------------------------------------- 188 * videobuf2 queue operations 189 */ 190 191static int mxc_isi_m2m_vb2_queue_setup(struct vb2_queue *q, 192 unsigned int *num_buffers, 193 unsigned int *num_planes, 194 unsigned int sizes[], 195 struct device *alloc_devs[]) 196{ 197 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 198 const struct mxc_isi_m2m_ctx_queue_data *qdata = 199 mxc_isi_m2m_ctx_qdata(ctx, q->type); 200 201 return mxc_isi_video_queue_setup(&qdata->format, qdata->info, 202 num_buffers, num_planes, sizes); 203} 204 205static int mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer *vb2) 206{ 207 struct vb2_queue *vq = vb2->vb2_queue; 208 struct mxc_isi_m2m_buffer *buf = to_isi_m2m_buffer(to_vb2_v4l2_buffer(vb2)); 209 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); 210 const struct mxc_isi_m2m_ctx_queue_data *qdata = 211 mxc_isi_m2m_ctx_qdata(ctx, vq->type); 212 213 mxc_isi_video_buffer_init(vb2, buf->dma_addrs, qdata->info, 214 &qdata->format); 215 216 return 0; 217} 218 219static int mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer *vb2) 220{ 221 struct vb2_queue *vq = vb2->vb2_queue; 222 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vq); 223 const struct mxc_isi_m2m_ctx_queue_data *qdata = 224 mxc_isi_m2m_ctx_qdata(ctx, vq->type); 225 226 return mxc_isi_video_buffer_prepare(ctx->m2m->isi, vb2, qdata->info, 227 &qdata->format); 228} 229 230static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2) 231{ 232 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); 233 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue); 234 235 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 236} 237 238static int mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue *q) 239{ 240 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 241 const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; 242 const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; 243 const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; 244 const struct mxc_isi_format_info *out_info = ctx->queues.out.info; 245 struct mxc_isi_m2m *m2m = ctx->m2m; 246 int ret; 247 248 guard(mutex)(&m2m->lock); 249 250 if (m2m->usage_count == INT_MAX) 251 return -EOVERFLOW; 252 253 ret = pm_runtime_resume_and_get(m2m->isi->dev); 254 if (ret) 255 return ret; 256 257 /* 258 * Acquire the pipe and initialize the channel with the first user of 259 * the M2M device. 260 */ 261 if (m2m->usage_count == 0) { 262 bool bypass = cap_pix->width == out_pix->width && 263 cap_pix->height == out_pix->height && 264 cap_info->encoding == out_info->encoding; 265 266 ret = mxc_isi_channel_acquire(m2m->pipe, 267 &mxc_isi_m2m_frame_write_done, 268 bypass); 269 if (ret) 270 goto err_pm; 271 272 mxc_isi_channel_get(m2m->pipe); 273 } 274 275 m2m->usage_count++; 276 277 /* 278 * Allocate resources for the channel, counting how many users require 279 * buffer chaining. 280 */ 281 if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { 282 ret = mxc_isi_channel_chain(m2m->pipe); 283 if (ret) 284 goto err_deinit; 285 286 m2m->chained_count++; 287 ctx->chained = true; 288 } 289 290 return 0; 291 292err_deinit: 293 if (--m2m->usage_count == 0) { 294 mxc_isi_channel_put(m2m->pipe); 295 mxc_isi_channel_release(m2m->pipe); 296 } 297err_pm: 298 pm_runtime_put(m2m->isi->dev); 299 return ret; 300} 301 302static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q, 303 unsigned int count) 304{ 305 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 306 struct mxc_isi_m2m_ctx_queue_data *qdata = 307 mxc_isi_m2m_ctx_qdata(ctx, q->type); 308 309 qdata->sequence = 0; 310 311 return 0; 312} 313 314static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q) 315{ 316 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 317 struct vb2_v4l2_buffer *vbuf; 318 319 for (;;) { 320 if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 321 vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 322 else 323 vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 324 if (!vbuf) 325 break; 326 327 v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); 328 } 329} 330 331static void mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue *q) 332{ 333 struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q); 334 struct mxc_isi_m2m *m2m = ctx->m2m; 335 336 guard(mutex)(&m2m->lock); 337 338 /* 339 * If the last context is this one, reset it to make sure the device 340 * will be reconfigured when streaming is restarted. 341 */ 342 if (m2m->last_ctx == ctx) 343 m2m->last_ctx = NULL; 344 345 /* Free the channel resources if this is the last chained context. */ 346 if (ctx->chained && --m2m->chained_count == 0) 347 mxc_isi_channel_unchain(m2m->pipe); 348 ctx->chained = false; 349 350 /* Turn off the light with the last user. */ 351 if (--m2m->usage_count == 0) { 352 mxc_isi_channel_disable(m2m->pipe); 353 mxc_isi_channel_put(m2m->pipe); 354 mxc_isi_channel_release(m2m->pipe); 355 } 356 357 WARN_ON(m2m->usage_count < 0); 358 359 pm_runtime_put(m2m->isi->dev); 360} 361 362static const struct vb2_ops mxc_isi_m2m_vb2_qops = { 363 .queue_setup = mxc_isi_m2m_vb2_queue_setup, 364 .buf_init = mxc_isi_m2m_vb2_buffer_init, 365 .buf_prepare = mxc_isi_m2m_vb2_buffer_prepare, 366 .buf_queue = mxc_isi_m2m_vb2_buffer_queue, 367 .prepare_streaming = mxc_isi_m2m_vb2_prepare_streaming, 368 .start_streaming = mxc_isi_m2m_vb2_start_streaming, 369 .stop_streaming = mxc_isi_m2m_vb2_stop_streaming, 370 .unprepare_streaming = mxc_isi_m2m_vb2_unprepare_streaming, 371}; 372 373static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq, 374 struct vb2_queue *dst_vq) 375{ 376 struct mxc_isi_m2m_ctx *ctx = priv; 377 struct mxc_isi_m2m *m2m = ctx->m2m; 378 int ret; 379 380 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 381 src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 382 src_vq->drv_priv = ctx; 383 src_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer); 384 src_vq->ops = &mxc_isi_m2m_vb2_qops; 385 src_vq->mem_ops = &vb2_dma_contig_memops; 386 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 387 src_vq->lock = &ctx->vb2_lock; 388 src_vq->dev = m2m->isi->dev; 389 390 ret = vb2_queue_init(src_vq); 391 if (ret) 392 return ret; 393 394 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 395 dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 396 dst_vq->drv_priv = ctx; 397 dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer); 398 dst_vq->ops = &mxc_isi_m2m_vb2_qops; 399 dst_vq->mem_ops = &vb2_dma_contig_memops; 400 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 401 dst_vq->lock = &ctx->vb2_lock; 402 dst_vq->dev = m2m->isi->dev; 403 404 return vb2_queue_init(dst_vq); 405} 406 407/* ----------------------------------------------------------------------------- 408 * V4L2 controls 409 */ 410 411static inline struct mxc_isi_m2m_ctx * 412ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl) 413{ 414 return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler); 415} 416 417static int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl) 418{ 419 struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl); 420 421 switch (ctrl->id) { 422 case V4L2_CID_HFLIP: 423 ctx->ctrls.hflip = ctrl->val; 424 break; 425 426 case V4L2_CID_VFLIP: 427 ctx->ctrls.vflip = ctrl->val; 428 break; 429 430 case V4L2_CID_ALPHA_COMPONENT: 431 ctx->ctrls.alpha = ctrl->val; 432 break; 433 } 434 435 return 0; 436} 437 438static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = { 439 .s_ctrl = mxc_isi_m2m_ctx_s_ctrl, 440}; 441 442static int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx) 443{ 444 struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler; 445 int ret; 446 447 v4l2_ctrl_handler_init(handler, 3); 448 449 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 450 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); 451 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 452 V4L2_CID_HFLIP, 0, 1, 1, 0); 453 v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops, 454 V4L2_CID_VFLIP, 0, 1, 1, 0); 455 456 if (handler->error) { 457 ret = handler->error; 458 v4l2_ctrl_handler_free(handler); 459 return ret; 460 } 461 462 ctx->fh.ctrl_handler = handler; 463 464 return 0; 465} 466 467static void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx) 468{ 469 v4l2_ctrl_handler_free(&ctx->ctrls.handler); 470} 471 472/* ----------------------------------------------------------------------------- 473 * V4L2 ioctls 474 */ 475 476static int mxc_isi_m2m_querycap(struct file *file, void *fh, 477 struct v4l2_capability *cap) 478{ 479 strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver)); 480 strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card)); 481 cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; 482 cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 483 484 return 0; 485} 486 487static int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh, 488 struct v4l2_fmtdesc *f) 489{ 490 const enum mxc_isi_video_type type = 491 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 492 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 493 const struct mxc_isi_format_info *info; 494 495 info = mxc_isi_format_enum(f->index, type); 496 if (!info) 497 return -EINVAL; 498 499 f->pixelformat = info->fourcc; 500 f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC 501 | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC; 502 503 return 0; 504} 505 506static const struct mxc_isi_format_info * 507__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, 508 struct v4l2_pix_format_mplane *pix, 509 const enum mxc_isi_video_type type) 510{ 511 if (type == MXC_ISI_VIDEO_M2M_CAP) { 512 /* Downscaling only */ 513 pix->width = min(pix->width, ctx->queues.out.format.width); 514 pix->height = min(pix->height, ctx->queues.out.format.height); 515 } 516 517 return mxc_isi_format_try(ctx->m2m->pipe, pix, type); 518} 519 520static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh, 521 struct v4l2_format *f) 522{ 523 const enum mxc_isi_video_type type = 524 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 525 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 526 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); 527 528 __mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type); 529 530 return 0; 531} 532 533static int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh, 534 struct v4l2_format *f) 535{ 536 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); 537 const struct mxc_isi_m2m_ctx_queue_data *qdata = 538 mxc_isi_m2m_ctx_qdata(ctx, f->type); 539 540 f->fmt.pix_mp = qdata->format; 541 542 return 0; 543} 544 545static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, 546 struct v4l2_format *f) 547{ 548 const enum mxc_isi_video_type type = 549 f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? 550 MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP; 551 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); 552 struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 553 const struct mxc_isi_format_info *info; 554 struct vb2_queue *vq; 555 556 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 557 558 if (vb2_is_busy(vq)) 559 return -EBUSY; 560 561 info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type); 562 563 if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 564 ctx->queues.out.format = *pix; 565 ctx->queues.out.info = info; 566 } 567 568 /* 569 * Always set the format on the capture side, due to either format 570 * propagation or direct setting. 571 */ 572 ctx->queues.cap.format = *pix; 573 ctx->queues.cap.info = info; 574 575 return 0; 576} 577 578static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = { 579 .vidioc_querycap = mxc_isi_m2m_querycap, 580 581 .vidioc_enum_fmt_vid_cap = mxc_isi_m2m_enum_fmt_vid, 582 .vidioc_enum_fmt_vid_out = mxc_isi_m2m_enum_fmt_vid, 583 .vidioc_g_fmt_vid_cap_mplane = mxc_isi_m2m_g_fmt_vid, 584 .vidioc_g_fmt_vid_out_mplane = mxc_isi_m2m_g_fmt_vid, 585 .vidioc_s_fmt_vid_cap_mplane = mxc_isi_m2m_s_fmt_vid, 586 .vidioc_s_fmt_vid_out_mplane = mxc_isi_m2m_s_fmt_vid, 587 .vidioc_try_fmt_vid_cap_mplane = mxc_isi_m2m_try_fmt_vid, 588 .vidioc_try_fmt_vid_out_mplane = mxc_isi_m2m_try_fmt_vid, 589 590 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 591 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 592 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 593 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 594 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 595 .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 596 .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 597 598 .vidioc_streamon = v4l2_m2m_ioctl_streamon, 599 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 600 601 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 602 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 603}; 604 605/* ----------------------------------------------------------------------------- 606 * Video device file operations 607 */ 608 609static void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx, 610 struct mxc_isi_m2m_ctx_queue_data *qdata, 611 enum mxc_isi_video_type type) 612{ 613 qdata->format.width = MXC_ISI_DEF_WIDTH; 614 qdata->format.height = MXC_ISI_DEF_HEIGHT; 615 qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT; 616 617 qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type); 618} 619 620static int mxc_isi_m2m_open(struct file *file) 621{ 622 struct video_device *vdev = video_devdata(file); 623 struct mxc_isi_m2m *m2m = video_drvdata(file); 624 struct mxc_isi_m2m_ctx *ctx; 625 int ret; 626 627 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 628 if (!ctx) 629 return -ENOMEM; 630 631 ctx->m2m = m2m; 632 mutex_init(&ctx->vb2_lock); 633 634 v4l2_fh_init(&ctx->fh, vdev); 635 636 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx, 637 &mxc_isi_m2m_queue_init); 638 if (IS_ERR(ctx->fh.m2m_ctx)) { 639 ret = PTR_ERR(ctx->fh.m2m_ctx); 640 ctx->fh.m2m_ctx = NULL; 641 goto err_fh; 642 } 643 644 mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT); 645 mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP); 646 647 ret = mxc_isi_m2m_ctx_ctrls_create(ctx); 648 if (ret) 649 goto err_ctx; 650 651 v4l2_fh_add(&ctx->fh, file); 652 653 return 0; 654 655err_ctx: 656 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 657err_fh: 658 v4l2_fh_exit(&ctx->fh); 659 mutex_destroy(&ctx->vb2_lock); 660 kfree(ctx); 661 return ret; 662} 663 664static int mxc_isi_m2m_release(struct file *file) 665{ 666 struct mxc_isi_m2m_ctx *ctx = file_to_isi_m2m_ctx(file); 667 668 v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 669 mxc_isi_m2m_ctx_ctrls_delete(ctx); 670 671 v4l2_fh_del(&ctx->fh, file); 672 v4l2_fh_exit(&ctx->fh); 673 674 mutex_destroy(&ctx->vb2_lock); 675 kfree(ctx); 676 677 return 0; 678} 679 680static const struct v4l2_file_operations mxc_isi_m2m_fops = { 681 .owner = THIS_MODULE, 682 .open = mxc_isi_m2m_open, 683 .release = mxc_isi_m2m_release, 684 .poll = v4l2_m2m_fop_poll, 685 .unlocked_ioctl = video_ioctl2, 686 .mmap = v4l2_m2m_fop_mmap, 687}; 688 689/* ----------------------------------------------------------------------------- 690 * Suspend & resume 691 */ 692 693void mxc_isi_m2m_suspend(struct mxc_isi_m2m *m2m) 694{ 695 if (m2m->usage_count == 0) 696 return; 697 698 v4l2_m2m_suspend(m2m->m2m_dev); 699 700 if (m2m->chained_count > 0) 701 mxc_isi_channel_unchain(m2m->pipe); 702 703 mxc_isi_channel_disable(m2m->pipe); 704 mxc_isi_channel_put(m2m->pipe); 705} 706 707int mxc_isi_m2m_resume(struct mxc_isi_m2m *m2m) 708{ 709 if (m2m->usage_count == 0) 710 return 0; 711 712 mxc_isi_channel_get(m2m->pipe); 713 714 if (m2m->chained_count > 0) 715 mxc_isi_channel_chain(m2m->pipe); 716 717 m2m->last_ctx = NULL; 718 v4l2_m2m_resume(m2m->m2m_dev); 719 720 return 0; 721} 722 723/* ----------------------------------------------------------------------------- 724 * Registration 725 */ 726 727int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev) 728{ 729 struct mxc_isi_m2m *m2m = &isi->m2m; 730 struct video_device *vdev = &m2m->vdev; 731 struct media_link *link; 732 int ret; 733 734 m2m->isi = isi; 735 m2m->pipe = &isi->pipes[0]; 736 737 mutex_init(&m2m->lock); 738 739 /* Initialize the video device and create controls. */ 740 snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m"); 741 742 vdev->fops = &mxc_isi_m2m_fops; 743 vdev->ioctl_ops = &mxc_isi_m2m_ioctl_ops; 744 vdev->v4l2_dev = v4l2_dev; 745 vdev->minor = -1; 746 vdev->release = video_device_release_empty; 747 vdev->vfl_dir = VFL_DIR_M2M; 748 749 vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; 750 video_set_drvdata(vdev, m2m); 751 752 /* Create the M2M device. */ 753 m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops); 754 if (IS_ERR(m2m->m2m_dev)) { 755 dev_err(isi->dev, "failed to initialize m2m device\n"); 756 ret = PTR_ERR(m2m->m2m_dev); 757 goto err_mutex; 758 } 759 760 /* Register the video device. */ 761 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 762 if (ret < 0) { 763 dev_err(isi->dev, "failed to register m2m device\n"); 764 goto err_m2m; 765 } 766 767 /* 768 * Populate the media graph. We can't use the mem2mem helper 769 * v4l2_m2m_register_media_controller() as the M2M interface needs to 770 * be connected to the existing entities in the graph, so we have to 771 * wire things up manually: 772 * 773 * - The entity in the video_device, which isn't touched by the V4L2 774 * core for M2M devices, is used as the source I/O entity in the 775 * graph, connected to the crossbar switch. 776 * 777 * - The video device at the end of the pipeline provides the sink 778 * entity, and is already wired up in the graph. 779 * 780 * - A new interface is created, pointing at both entities. The sink 781 * entity will thus have two interfaces pointing to it. 782 */ 783 m2m->pad.flags = MEDIA_PAD_FL_SOURCE; 784 vdev->entity.name = "mxc_isi.output"; 785 vdev->entity.function = MEDIA_ENT_F_IO_V4L; 786 ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad); 787 if (ret) 788 goto err_video; 789 790 ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity); 791 if (ret) 792 goto err_entity_cleanup; 793 794 ret = media_create_pad_link(&vdev->entity, 0, 795 &m2m->isi->crossbar.sd.entity, 796 m2m->isi->crossbar.num_sinks - 1, 797 MEDIA_LNK_FL_IMMUTABLE | 798 MEDIA_LNK_FL_ENABLED); 799 if (ret) 800 goto err_entity_unreg; 801 802 m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO, 803 0, VIDEO_MAJOR, vdev->minor); 804 if (!m2m->intf) { 805 ret = -ENOMEM; 806 goto err_entity_unreg; 807 } 808 809 link = media_create_intf_link(&vdev->entity, &m2m->intf->intf, 810 MEDIA_LNK_FL_IMMUTABLE | 811 MEDIA_LNK_FL_ENABLED); 812 if (!link) { 813 ret = -ENOMEM; 814 goto err_devnode; 815 } 816 817 link = media_create_intf_link(&m2m->pipe->video.vdev.entity, 818 &m2m->intf->intf, 819 MEDIA_LNK_FL_IMMUTABLE | 820 MEDIA_LNK_FL_ENABLED); 821 if (!link) { 822 ret = -ENOMEM; 823 goto err_devnode; 824 } 825 826 return 0; 827 828err_devnode: 829 media_devnode_remove(m2m->intf); 830err_entity_unreg: 831 media_device_unregister_entity(&vdev->entity); 832err_entity_cleanup: 833 media_entity_cleanup(&vdev->entity); 834err_video: 835 video_unregister_device(vdev); 836err_m2m: 837 v4l2_m2m_release(m2m->m2m_dev); 838err_mutex: 839 mutex_destroy(&m2m->lock); 840 return ret; 841} 842 843int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi) 844{ 845 struct mxc_isi_m2m *m2m = &isi->m2m; 846 struct video_device *vdev = &m2m->vdev; 847 848 video_unregister_device(vdev); 849 850 v4l2_m2m_release(m2m->m2m_dev); 851 media_devnode_remove(m2m->intf); 852 media_entity_cleanup(&vdev->entity); 853 mutex_destroy(&m2m->lock); 854 855 return 0; 856}
~/
Read-only file preview.