162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Coda multi-standard codec IP 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Vista Silicon S.L. 662306a36Sopenharmony_ci * Javier Martin, <javier.martin@vista-silicon.com> 762306a36Sopenharmony_ci * Xavier Duret 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/firmware.h> 1462306a36Sopenharmony_ci#include <linux/gcd.h> 1562306a36Sopenharmony_ci#include <linux/genalloc.h> 1662306a36Sopenharmony_ci#include <linux/idr.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/irq.h> 2062306a36Sopenharmony_ci#include <linux/kfifo.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci#include <linux/of_platform.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/videodev2.h> 2862306a36Sopenharmony_ci#include <linux/ratelimit.h> 2962306a36Sopenharmony_ci#include <linux/reset.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3262306a36Sopenharmony_ci#include <media/v4l2-device.h> 3362306a36Sopenharmony_ci#include <media/v4l2-event.h> 3462306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3562306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 3662306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3762306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3862306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "coda.h" 4162306a36Sopenharmony_ci#include "imx-vdoa.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define CODA_NAME "coda" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define CODADX6_MAX_INSTANCES 4 4662306a36Sopenharmony_ci#define CODA_MAX_FORMATS 5 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define CODA_ISRAM_SIZE (2048 * 2) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define MIN_W 48 5162306a36Sopenharmony_ci#define MIN_H 16 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define S_ALIGN 1 /* multiple of 2 */ 5462306a36Sopenharmony_ci#define W_ALIGN 1 /* multiple of 2 */ 5562306a36Sopenharmony_ci#define H_ALIGN 1 /* multiple of 2 */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciint coda_debug; 6062306a36Sopenharmony_cimodule_param(coda_debug, int, 0644); 6162306a36Sopenharmony_ciMODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int disable_tiling; 6462306a36Sopenharmony_cimodule_param(disable_tiling, int, 0644); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int disable_vdoa; 6862306a36Sopenharmony_cimodule_param(disable_vdoa, int, 0644); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int enable_bwb = 0; 7262306a36Sopenharmony_cimodule_param(enable_bwb, int, 0644); 7362306a36Sopenharmony_ciMODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain streams"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_civoid coda_write(struct coda_dev *dev, u32 data, u32 reg) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci v4l2_dbg(3, coda_debug, &dev->v4l2_dev, 7862306a36Sopenharmony_ci "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); 7962306a36Sopenharmony_ci writel(data, dev->regs_base + reg); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ciunsigned int coda_read(struct coda_dev *dev, u32 reg) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci u32 data; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci data = readl(dev->regs_base + reg); 8762306a36Sopenharmony_ci v4l2_dbg(3, coda_debug, &dev->v4l2_dev, 8862306a36Sopenharmony_ci "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); 8962306a36Sopenharmony_ci return data; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, 9362306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf, unsigned int reg_y) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); 9662306a36Sopenharmony_ci u32 base_cb, base_cr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci switch (q_data->fourcc) { 9962306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 10062306a36Sopenharmony_ci /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */ 10162306a36Sopenharmony_ci case V4L2_PIX_FMT_NV12: 10262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 10362306a36Sopenharmony_ci default: 10462306a36Sopenharmony_ci base_cb = base_y + q_data->bytesperline * q_data->height; 10562306a36Sopenharmony_ci base_cr = base_cb + q_data->bytesperline * q_data->height / 4; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 10862306a36Sopenharmony_ci /* Switch Cb and Cr for YVU420 format */ 10962306a36Sopenharmony_ci base_cr = base_y + q_data->bytesperline * q_data->height; 11062306a36Sopenharmony_ci base_cb = base_cr + q_data->bytesperline * q_data->height / 4; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 11362306a36Sopenharmony_ci base_cb = base_y + q_data->bytesperline * q_data->height; 11462306a36Sopenharmony_ci base_cr = base_cb + q_data->bytesperline * q_data->height / 2; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci coda_write(ctx->dev, base_y, reg_y); 11862306a36Sopenharmony_ci coda_write(ctx->dev, base_cb, reg_y + 4); 11962306a36Sopenharmony_ci coda_write(ctx->dev, base_cr, reg_y + 8); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ 12362306a36Sopenharmony_ci { mode, src_fourcc, dst_fourcc, max_w, max_h } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * Arrays of codecs supported by each given version of Coda: 12762306a36Sopenharmony_ci * i.MX27 -> codadx6 12862306a36Sopenharmony_ci * i.MX51 -> codahx4 12962306a36Sopenharmony_ci * i.MX53 -> coda7 13062306a36Sopenharmony_ci * i.MX6 -> coda960 13162306a36Sopenharmony_ci * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic const struct coda_codec codadx6_codecs[] = { 13462306a36Sopenharmony_ci CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), 13562306a36Sopenharmony_ci CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct coda_codec codahx4_codecs[] = { 13962306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), 14062306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), 14162306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), 14262306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720), 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct coda_codec coda7_codecs[] = { 14662306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), 14762306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), 14862306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), 14962306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), 15062306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), 15162306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), 15262306a36Sopenharmony_ci CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct coda_codec coda9_codecs[] = { 15662306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088), 15762306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088), 15862306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192), 15962306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088), 16062306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088), 16162306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088), 16262306a36Sopenharmony_ci CODA_CODEC(CODA9_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192), 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistruct coda_video_device { 16662306a36Sopenharmony_ci const char *name; 16762306a36Sopenharmony_ci enum coda_inst_type type; 16862306a36Sopenharmony_ci const struct coda_context_ops *ops; 16962306a36Sopenharmony_ci bool direct; 17062306a36Sopenharmony_ci u32 src_formats[CODA_MAX_FORMATS]; 17162306a36Sopenharmony_ci u32 dst_formats[CODA_MAX_FORMATS]; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic const struct coda_video_device coda_bit_encoder = { 17562306a36Sopenharmony_ci .name = "coda-video-encoder", 17662306a36Sopenharmony_ci .type = CODA_INST_ENCODER, 17762306a36Sopenharmony_ci .ops = &coda_bit_encode_ops, 17862306a36Sopenharmony_ci .src_formats = { 17962306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 18062306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 18162306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci .dst_formats = { 18462306a36Sopenharmony_ci V4L2_PIX_FMT_H264, 18562306a36Sopenharmony_ci V4L2_PIX_FMT_MPEG4, 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct coda_video_device coda_bit_jpeg_encoder = { 19062306a36Sopenharmony_ci .name = "coda-jpeg-encoder", 19162306a36Sopenharmony_ci .type = CODA_INST_ENCODER, 19262306a36Sopenharmony_ci .ops = &coda_bit_encode_ops, 19362306a36Sopenharmony_ci .src_formats = { 19462306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 19562306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 19662306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 19762306a36Sopenharmony_ci V4L2_PIX_FMT_YUV422P, 19862306a36Sopenharmony_ci }, 19962306a36Sopenharmony_ci .dst_formats = { 20062306a36Sopenharmony_ci V4L2_PIX_FMT_JPEG, 20162306a36Sopenharmony_ci }, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic const struct coda_video_device coda_bit_decoder = { 20562306a36Sopenharmony_ci .name = "coda-video-decoder", 20662306a36Sopenharmony_ci .type = CODA_INST_DECODER, 20762306a36Sopenharmony_ci .ops = &coda_bit_decode_ops, 20862306a36Sopenharmony_ci .src_formats = { 20962306a36Sopenharmony_ci V4L2_PIX_FMT_H264, 21062306a36Sopenharmony_ci V4L2_PIX_FMT_MPEG2, 21162306a36Sopenharmony_ci V4L2_PIX_FMT_MPEG4, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci .dst_formats = { 21462306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 21562306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 21662306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * If V4L2_PIX_FMT_YUYV should be default, 21962306a36Sopenharmony_ci * set_default_params() must be adjusted. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci V4L2_PIX_FMT_YUYV, 22262306a36Sopenharmony_ci }, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct coda_video_device coda_bit_jpeg_decoder = { 22662306a36Sopenharmony_ci .name = "coda-jpeg-decoder", 22762306a36Sopenharmony_ci .type = CODA_INST_DECODER, 22862306a36Sopenharmony_ci .ops = &coda_bit_decode_ops, 22962306a36Sopenharmony_ci .src_formats = { 23062306a36Sopenharmony_ci V4L2_PIX_FMT_JPEG, 23162306a36Sopenharmony_ci }, 23262306a36Sopenharmony_ci .dst_formats = { 23362306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 23462306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 23562306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 23662306a36Sopenharmony_ci V4L2_PIX_FMT_YUV422P, 23762306a36Sopenharmony_ci }, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct coda_video_device coda9_jpeg_encoder = { 24162306a36Sopenharmony_ci .name = "coda-jpeg-encoder", 24262306a36Sopenharmony_ci .type = CODA_INST_ENCODER, 24362306a36Sopenharmony_ci .ops = &coda9_jpeg_encode_ops, 24462306a36Sopenharmony_ci .direct = true, 24562306a36Sopenharmony_ci .src_formats = { 24662306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 24762306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 24862306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 24962306a36Sopenharmony_ci V4L2_PIX_FMT_YUV422P, 25062306a36Sopenharmony_ci V4L2_PIX_FMT_GREY, 25162306a36Sopenharmony_ci }, 25262306a36Sopenharmony_ci .dst_formats = { 25362306a36Sopenharmony_ci V4L2_PIX_FMT_JPEG, 25462306a36Sopenharmony_ci }, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic const struct coda_video_device coda9_jpeg_decoder = { 25862306a36Sopenharmony_ci .name = "coda-jpeg-decoder", 25962306a36Sopenharmony_ci .type = CODA_INST_DECODER, 26062306a36Sopenharmony_ci .ops = &coda9_jpeg_decode_ops, 26162306a36Sopenharmony_ci .direct = true, 26262306a36Sopenharmony_ci .src_formats = { 26362306a36Sopenharmony_ci V4L2_PIX_FMT_JPEG, 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci .dst_formats = { 26662306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 26762306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 26862306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 26962306a36Sopenharmony_ci V4L2_PIX_FMT_YUV422P, 27062306a36Sopenharmony_ci }, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct coda_video_device *codadx6_video_devices[] = { 27462306a36Sopenharmony_ci &coda_bit_encoder, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct coda_video_device *codahx4_video_devices[] = { 27862306a36Sopenharmony_ci &coda_bit_encoder, 27962306a36Sopenharmony_ci &coda_bit_decoder, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const struct coda_video_device *coda7_video_devices[] = { 28362306a36Sopenharmony_ci &coda_bit_jpeg_encoder, 28462306a36Sopenharmony_ci &coda_bit_jpeg_decoder, 28562306a36Sopenharmony_ci &coda_bit_encoder, 28662306a36Sopenharmony_ci &coda_bit_decoder, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const struct coda_video_device *coda9_video_devices[] = { 29062306a36Sopenharmony_ci &coda9_jpeg_encoder, 29162306a36Sopenharmony_ci &coda9_jpeg_decoder, 29262306a36Sopenharmony_ci &coda_bit_encoder, 29362306a36Sopenharmony_ci &coda_bit_decoder, 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* 29762306a36Sopenharmony_ci * Normalize all supported YUV 4:2:0 formats to the value used in the codec 29862306a36Sopenharmony_ci * tables. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic u32 coda_format_normalize_yuv(u32 fourcc) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci switch (fourcc) { 30362306a36Sopenharmony_ci case V4L2_PIX_FMT_NV12: 30462306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 30562306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 30662306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 30762306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 30862306a36Sopenharmony_ci return V4L2_PIX_FMT_YUV420; 30962306a36Sopenharmony_ci default: 31062306a36Sopenharmony_ci return fourcc; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic const struct coda_codec *coda_find_codec(struct coda_dev *dev, 31562306a36Sopenharmony_ci int src_fourcc, int dst_fourcc) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci const struct coda_codec *codecs = dev->devtype->codecs; 31862306a36Sopenharmony_ci int num_codecs = dev->devtype->num_codecs; 31962306a36Sopenharmony_ci int k; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci src_fourcc = coda_format_normalize_yuv(src_fourcc); 32262306a36Sopenharmony_ci dst_fourcc = coda_format_normalize_yuv(dst_fourcc); 32362306a36Sopenharmony_ci if (src_fourcc == dst_fourcc) 32462306a36Sopenharmony_ci return NULL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (k = 0; k < num_codecs; k++) { 32762306a36Sopenharmony_ci if (codecs[k].src_fourcc == src_fourcc && 32862306a36Sopenharmony_ci codecs[k].dst_fourcc == dst_fourcc) 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (k == num_codecs) 33362306a36Sopenharmony_ci return NULL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return &codecs[k]; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void coda_get_max_dimensions(struct coda_dev *dev, 33962306a36Sopenharmony_ci const struct coda_codec *codec, 34062306a36Sopenharmony_ci int *max_w, int *max_h) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci const struct coda_codec *codecs = dev->devtype->codecs; 34362306a36Sopenharmony_ci int num_codecs = dev->devtype->num_codecs; 34462306a36Sopenharmony_ci unsigned int w, h; 34562306a36Sopenharmony_ci int k; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (codec) { 34862306a36Sopenharmony_ci w = codec->max_w; 34962306a36Sopenharmony_ci h = codec->max_h; 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci for (k = 0, w = 0, h = 0; k < num_codecs; k++) { 35262306a36Sopenharmony_ci w = max(w, codecs[k].max_w); 35362306a36Sopenharmony_ci h = max(h, codecs[k].max_h); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (max_w) 35862306a36Sopenharmony_ci *max_w = w; 35962306a36Sopenharmony_ci if (max_h) 36062306a36Sopenharmony_ci *max_h = h; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic const struct coda_video_device *to_coda_video_device(struct video_device 36462306a36Sopenharmony_ci *vdev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct coda_dev *dev = video_get_drvdata(vdev); 36762306a36Sopenharmony_ci unsigned int i = vdev - dev->vfd; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (i >= dev->devtype->num_vdevs) 37062306a36Sopenharmony_ci return NULL; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return dev->devtype->vdevs[i]; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciconst char *coda_product_name(int product) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci static char buf[9]; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (product) { 38062306a36Sopenharmony_ci case CODA_DX6: 38162306a36Sopenharmony_ci return "CodaDx6"; 38262306a36Sopenharmony_ci case CODA_HX4: 38362306a36Sopenharmony_ci return "CodaHx4"; 38462306a36Sopenharmony_ci case CODA_7541: 38562306a36Sopenharmony_ci return "CODA7541"; 38662306a36Sopenharmony_ci case CODA_960: 38762306a36Sopenharmony_ci return "CODA960"; 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "(0x%04x)", product); 39062306a36Sopenharmony_ci return buf; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic struct vdoa_data *coda_get_vdoa_data(void) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct device_node *vdoa_node; 39762306a36Sopenharmony_ci struct platform_device *vdoa_pdev; 39862306a36Sopenharmony_ci struct vdoa_data *vdoa_data = NULL; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa"); 40162306a36Sopenharmony_ci if (!vdoa_node) 40262306a36Sopenharmony_ci return NULL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci vdoa_pdev = of_find_device_by_node(vdoa_node); 40562306a36Sopenharmony_ci if (!vdoa_pdev) 40662306a36Sopenharmony_ci goto out; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci vdoa_data = platform_get_drvdata(vdoa_pdev); 40962306a36Sopenharmony_ci if (!vdoa_data) 41062306a36Sopenharmony_ci vdoa_data = ERR_PTR(-EPROBE_DEFER); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci put_device(&vdoa_pdev->dev); 41362306a36Sopenharmony_ciout: 41462306a36Sopenharmony_ci of_node_put(vdoa_node); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return vdoa_data; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* 42062306a36Sopenharmony_ci * V4L2 ioctl() operations. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic int coda_querycap(struct file *file, void *priv, 42362306a36Sopenharmony_ci struct v4l2_capability *cap) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci strscpy(cap->driver, CODA_NAME, sizeof(cap->driver)); 42862306a36Sopenharmony_ci strscpy(cap->card, coda_product_name(ctx->dev->devtype->product), 42962306a36Sopenharmony_ci sizeof(cap->card)); 43062306a36Sopenharmony_ci strscpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic const u32 coda_formats_420[CODA_MAX_FORMATS] = { 43562306a36Sopenharmony_ci V4L2_PIX_FMT_NV12, 43662306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420, 43762306a36Sopenharmony_ci V4L2_PIX_FMT_YVU420, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int coda_enum_fmt(struct file *file, void *priv, 44162306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 44462306a36Sopenharmony_ci const struct coda_video_device *cvd = to_coda_video_device(vdev); 44562306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 44662306a36Sopenharmony_ci const u32 *formats; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 44962306a36Sopenharmony_ci formats = cvd->src_formats; 45062306a36Sopenharmony_ci else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 45162306a36Sopenharmony_ci struct coda_q_data *q_data_src; 45262306a36Sopenharmony_ci struct vb2_queue *src_vq; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci formats = cvd->dst_formats; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * If the source format is already fixed, only allow the same 45862306a36Sopenharmony_ci * chroma subsampling. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 46162306a36Sopenharmony_ci src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 46262306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT); 46362306a36Sopenharmony_ci if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && 46462306a36Sopenharmony_ci vb2_is_streaming(src_vq)) { 46562306a36Sopenharmony_ci if (ctx->params.jpeg_chroma_subsampling == 46662306a36Sopenharmony_ci V4L2_JPEG_CHROMA_SUBSAMPLING_420) { 46762306a36Sopenharmony_ci formats = coda_formats_420; 46862306a36Sopenharmony_ci } else if (ctx->params.jpeg_chroma_subsampling == 46962306a36Sopenharmony_ci V4L2_JPEG_CHROMA_SUBSAMPLING_422) { 47062306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUV422P; 47162306a36Sopenharmony_ci return f->index ? -EINVAL : 0; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) 47962306a36Sopenharmony_ci return -EINVAL; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Skip YUYV if the vdoa is not available */ 48262306a36Sopenharmony_ci if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && 48362306a36Sopenharmony_ci formats[f->index] == V4L2_PIX_FMT_YUYV) 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci f->pixelformat = formats[f->index]; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int coda_g_fmt(struct file *file, void *priv, 49262306a36Sopenharmony_ci struct v4l2_format *f) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct coda_q_data *q_data; 49562306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 49862306a36Sopenharmony_ci if (!q_data) 49962306a36Sopenharmony_ci return -EINVAL; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 50262306a36Sopenharmony_ci f->fmt.pix.pixelformat = q_data->fourcc; 50362306a36Sopenharmony_ci f->fmt.pix.width = q_data->width; 50462306a36Sopenharmony_ci f->fmt.pix.height = q_data->height; 50562306a36Sopenharmony_ci f->fmt.pix.bytesperline = q_data->bytesperline; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci f->fmt.pix.sizeimage = q_data->sizeimage; 50862306a36Sopenharmony_ci f->fmt.pix.colorspace = ctx->colorspace; 50962306a36Sopenharmony_ci f->fmt.pix.xfer_func = ctx->xfer_func; 51062306a36Sopenharmony_ci f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; 51162306a36Sopenharmony_ci f->fmt.pix.quantization = ctx->quantization; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct coda_q_data *q_data; 51962306a36Sopenharmony_ci const u32 *formats; 52062306a36Sopenharmony_ci int i; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 52362306a36Sopenharmony_ci formats = ctx->cvd->src_formats; 52462306a36Sopenharmony_ci else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 52562306a36Sopenharmony_ci formats = ctx->cvd->dst_formats; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci for (i = 0; i < CODA_MAX_FORMATS; i++) { 53062306a36Sopenharmony_ci /* Skip YUYV if the vdoa is not available */ 53162306a36Sopenharmony_ci if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && 53262306a36Sopenharmony_ci formats[i] == V4L2_PIX_FMT_YUYV) 53362306a36Sopenharmony_ci continue; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (formats[i] == f->fmt.pix.pixelformat) { 53662306a36Sopenharmony_ci f->fmt.pix.pixelformat = formats[i]; 53762306a36Sopenharmony_ci return 0; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Fall back to currently set pixelformat */ 54262306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 54362306a36Sopenharmony_ci f->fmt.pix.pixelformat = q_data->fourcc; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f, 54962306a36Sopenharmony_ci bool *use_vdoa) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci int err; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (!use_vdoa) 55762306a36Sopenharmony_ci return -EINVAL; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (!ctx->vdoa) { 56062306a36Sopenharmony_ci *use_vdoa = false; 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci err = vdoa_context_configure(NULL, round_up(f->fmt.pix.width, 16), 56562306a36Sopenharmony_ci f->fmt.pix.height, f->fmt.pix.pixelformat); 56662306a36Sopenharmony_ci if (err) { 56762306a36Sopenharmony_ci *use_vdoa = false; 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci *use_vdoa = true; 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, 57662306a36Sopenharmony_ci u32 width, u32 height) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * This is a rough estimate for sensible compressed buffer 58062306a36Sopenharmony_ci * sizes (between 1 and 16 bits per pixel). This could be 58162306a36Sopenharmony_ci * improved by better format specific worst case estimates. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci return round_up(clamp(sizeimage, width * height / 8, 58462306a36Sopenharmony_ci width * height * 2), PAGE_SIZE); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, 58862306a36Sopenharmony_ci struct v4l2_format *f) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 59162306a36Sopenharmony_ci unsigned int max_w, max_h; 59262306a36Sopenharmony_ci enum v4l2_field field; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci field = f->fmt.pix.field; 59562306a36Sopenharmony_ci if (field == V4L2_FIELD_ANY) 59662306a36Sopenharmony_ci field = V4L2_FIELD_NONE; 59762306a36Sopenharmony_ci else if (V4L2_FIELD_NONE != field) 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* V4L2 specification suggests the driver corrects the format struct 60162306a36Sopenharmony_ci * if any of the dimensions is unsupported */ 60262306a36Sopenharmony_ci f->fmt.pix.field = field; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci coda_get_max_dimensions(dev, codec, &max_w, &max_h); 60562306a36Sopenharmony_ci v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, 60662306a36Sopenharmony_ci &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, 60762306a36Sopenharmony_ci S_ALIGN); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci switch (f->fmt.pix.pixelformat) { 61062306a36Sopenharmony_ci case V4L2_PIX_FMT_NV12: 61162306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 61262306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Frame stride must be at least multiple of 8, 61562306a36Sopenharmony_ci * but multiple of 16 for h.264 or JPEG 4:2:x 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); 61862306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 61962306a36Sopenharmony_ci f->fmt.pix.height * 3 / 2; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 62262306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; 62362306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 62462306a36Sopenharmony_ci f->fmt.pix.height; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 62762306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); 62862306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 62962306a36Sopenharmony_ci f->fmt.pix.height * 2; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 63262306a36Sopenharmony_ci /* keep 16 pixel alignment of 8-bit pixel data */ 63362306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16); 63462306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case V4L2_PIX_FMT_JPEG: 63762306a36Sopenharmony_ci case V4L2_PIX_FMT_H264: 63862306a36Sopenharmony_ci case V4L2_PIX_FMT_MPEG4: 63962306a36Sopenharmony_ci case V4L2_PIX_FMT_MPEG2: 64062306a36Sopenharmony_ci f->fmt.pix.bytesperline = 0; 64162306a36Sopenharmony_ci f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, 64262306a36Sopenharmony_ci f->fmt.pix.sizeimage, 64362306a36Sopenharmony_ci f->fmt.pix.width, 64462306a36Sopenharmony_ci f->fmt.pix.height); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci default: 64762306a36Sopenharmony_ci BUG(); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int coda_try_fmt_vid_cap(struct file *file, void *priv, 65462306a36Sopenharmony_ci struct v4l2_format *f) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 65762306a36Sopenharmony_ci const struct coda_q_data *q_data_src; 65862306a36Sopenharmony_ci const struct coda_codec *codec; 65962306a36Sopenharmony_ci struct vb2_queue *src_vq; 66062306a36Sopenharmony_ci int hscale = 0; 66162306a36Sopenharmony_ci int vscale = 0; 66262306a36Sopenharmony_ci int ret; 66362306a36Sopenharmony_ci bool use_vdoa; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = coda_try_pixelformat(ctx, f); 66662306a36Sopenharmony_ci if (ret < 0) 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * If the source format is already fixed, only allow the same output 67362306a36Sopenharmony_ci * resolution. When decoding JPEG images, we also have to make sure to 67462306a36Sopenharmony_ci * use the same chroma subsampling. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 67762306a36Sopenharmony_ci if (vb2_is_streaming(src_vq)) { 67862306a36Sopenharmony_ci if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && 67962306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_960) { 68062306a36Sopenharmony_ci hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); 68162306a36Sopenharmony_ci vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci f->fmt.pix.width = q_data_src->width >> hscale; 68462306a36Sopenharmony_ci f->fmt.pix.height = q_data_src->height >> vscale; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { 68762306a36Sopenharmony_ci if (ctx->params.jpeg_chroma_subsampling == 68862306a36Sopenharmony_ci V4L2_JPEG_CHROMA_SUBSAMPLING_420 && 68962306a36Sopenharmony_ci f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) 69062306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; 69162306a36Sopenharmony_ci else if (ctx->params.jpeg_chroma_subsampling == 69262306a36Sopenharmony_ci V4L2_JPEG_CHROMA_SUBSAMPLING_422) 69362306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci f->fmt.pix.colorspace = ctx->colorspace; 69862306a36Sopenharmony_ci f->fmt.pix.xfer_func = ctx->xfer_func; 69962306a36Sopenharmony_ci f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; 70062306a36Sopenharmony_ci f->fmt.pix.quantization = ctx->quantization; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 70362306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, q_data_src->fourcc, 70462306a36Sopenharmony_ci f->fmt.pix.pixelformat); 70562306a36Sopenharmony_ci if (!codec) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = coda_try_fmt(ctx, codec, f); 70962306a36Sopenharmony_ci if (ret < 0) 71062306a36Sopenharmony_ci return ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* The decoders always write complete macroblocks or MCUs */ 71362306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER) { 71462306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16 >> hscale); 71562306a36Sopenharmony_ci f->fmt.pix.height = round_up(f->fmt.pix.height, 16 >> vscale); 71662306a36Sopenharmony_ci if (codec->src_fourcc == V4L2_PIX_FMT_JPEG && 71762306a36Sopenharmony_ci f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { 71862306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 71962306a36Sopenharmony_ci f->fmt.pix.height * 2; 72062306a36Sopenharmony_ci } else { 72162306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 72262306a36Sopenharmony_ci f->fmt.pix.height * 3 / 2; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa); 72662306a36Sopenharmony_ci if (ret < 0) 72762306a36Sopenharmony_ci return ret; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { 73062306a36Sopenharmony_ci if (!use_vdoa) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2; 73462306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 73562306a36Sopenharmony_ci f->fmt.pix.height; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic void coda_set_default_colorspace(struct v4l2_pix_format *fmt) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci enum v4l2_colorspace colorspace; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (fmt->pixelformat == V4L2_PIX_FMT_JPEG) 74762306a36Sopenharmony_ci colorspace = V4L2_COLORSPACE_JPEG; 74862306a36Sopenharmony_ci else if (fmt->width <= 720 && fmt->height <= 576) 74962306a36Sopenharmony_ci colorspace = V4L2_COLORSPACE_SMPTE170M; 75062306a36Sopenharmony_ci else 75162306a36Sopenharmony_ci colorspace = V4L2_COLORSPACE_REC709; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci fmt->colorspace = colorspace; 75462306a36Sopenharmony_ci fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; 75562306a36Sopenharmony_ci fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 75662306a36Sopenharmony_ci fmt->quantization = V4L2_QUANTIZATION_DEFAULT; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int coda_try_fmt_vid_out(struct file *file, void *priv, 76062306a36Sopenharmony_ci struct v4l2_format *f) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 76362306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 76462306a36Sopenharmony_ci const struct coda_q_data *q_data_dst; 76562306a36Sopenharmony_ci const struct coda_codec *codec; 76662306a36Sopenharmony_ci int ret; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci ret = coda_try_pixelformat(ctx, f); 76962306a36Sopenharmony_ci if (ret < 0) 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) 77362306a36Sopenharmony_ci coda_set_default_colorspace(&f->fmt.pix); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 77662306a36Sopenharmony_ci codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return coda_try_fmt(ctx, codec, f); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, 78262306a36Sopenharmony_ci struct v4l2_rect *r) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct coda_q_data *q_data; 78562306a36Sopenharmony_ci struct vb2_queue *vq; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 78862306a36Sopenharmony_ci if (!vq) 78962306a36Sopenharmony_ci return -EINVAL; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci q_data = get_q_data(ctx, f->type); 79262306a36Sopenharmony_ci if (!q_data) 79362306a36Sopenharmony_ci return -EINVAL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 79662306a36Sopenharmony_ci v4l2_err(&ctx->dev->v4l2_dev, "%s: %s queue busy: %d\n", 79762306a36Sopenharmony_ci __func__, v4l2_type_names[f->type], vq->num_buffers); 79862306a36Sopenharmony_ci return -EBUSY; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci q_data->fourcc = f->fmt.pix.pixelformat; 80262306a36Sopenharmony_ci q_data->width = f->fmt.pix.width; 80362306a36Sopenharmony_ci q_data->height = f->fmt.pix.height; 80462306a36Sopenharmony_ci q_data->bytesperline = f->fmt.pix.bytesperline; 80562306a36Sopenharmony_ci q_data->sizeimage = f->fmt.pix.sizeimage; 80662306a36Sopenharmony_ci if (r) { 80762306a36Sopenharmony_ci q_data->rect = *r; 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci q_data->rect.left = 0; 81062306a36Sopenharmony_ci q_data->rect.top = 0; 81162306a36Sopenharmony_ci q_data->rect.width = f->fmt.pix.width; 81262306a36Sopenharmony_ci q_data->rect.height = f->fmt.pix.height; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci switch (f->fmt.pix.pixelformat) { 81662306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 81762306a36Sopenharmony_ci ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case V4L2_PIX_FMT_NV12: 82062306a36Sopenharmony_ci if (!disable_tiling && ctx->use_bit && 82162306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_960) { 82262306a36Sopenharmony_ci ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci fallthrough; 82662306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 82762306a36Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 82862306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 82962306a36Sopenharmony_ci ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci default: 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP && 83662306a36Sopenharmony_ci !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) && 83762306a36Sopenharmony_ci ctx->use_vdoa) 83862306a36Sopenharmony_ci vdoa_context_configure(ctx->vdoa, 83962306a36Sopenharmony_ci round_up(f->fmt.pix.width, 16), 84062306a36Sopenharmony_ci f->fmt.pix.height, 84162306a36Sopenharmony_ci f->fmt.pix.pixelformat); 84262306a36Sopenharmony_ci else 84362306a36Sopenharmony_ci ctx->use_vdoa = false; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci coda_dbg(1, ctx, "Setting %s format, wxh: %dx%d, fmt: %4.4s %c\n", 84662306a36Sopenharmony_ci v4l2_type_names[f->type], q_data->width, q_data->height, 84762306a36Sopenharmony_ci (char *)&q_data->fourcc, 84862306a36Sopenharmony_ci (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) ? 'L' : 'T'); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic int coda_s_fmt_vid_cap(struct file *file, void *priv, 85462306a36Sopenharmony_ci struct v4l2_format *f) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 85762306a36Sopenharmony_ci struct coda_q_data *q_data_src; 85862306a36Sopenharmony_ci const struct coda_codec *codec; 85962306a36Sopenharmony_ci struct v4l2_rect r; 86062306a36Sopenharmony_ci int hscale = 0; 86162306a36Sopenharmony_ci int vscale = 0; 86262306a36Sopenharmony_ci int ret; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && 86762306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_960) { 86862306a36Sopenharmony_ci hscale = coda_jpeg_scale(q_data_src->width, f->fmt.pix.width); 86962306a36Sopenharmony_ci vscale = coda_jpeg_scale(q_data_src->height, f->fmt.pix.height); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci ret = coda_try_fmt_vid_cap(file, priv, f); 87362306a36Sopenharmony_ci if (ret) 87462306a36Sopenharmony_ci return ret; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci r.left = 0; 87762306a36Sopenharmony_ci r.top = 0; 87862306a36Sopenharmony_ci r.width = q_data_src->width >> hscale; 87962306a36Sopenharmony_ci r.height = q_data_src->height >> vscale; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci ret = coda_s_fmt(ctx, f, &r); 88262306a36Sopenharmony_ci if (ret) 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (ctx->inst_type != CODA_INST_ENCODER) 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Setting the coded format determines the selected codec */ 88962306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, q_data_src->fourcc, 89062306a36Sopenharmony_ci f->fmt.pix.pixelformat); 89162306a36Sopenharmony_ci if (!codec) { 89262306a36Sopenharmony_ci v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); 89362306a36Sopenharmony_ci return -EINVAL; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci ctx->codec = codec; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci ctx->colorspace = f->fmt.pix.colorspace; 89862306a36Sopenharmony_ci ctx->xfer_func = f->fmt.pix.xfer_func; 89962306a36Sopenharmony_ci ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; 90062306a36Sopenharmony_ci ctx->quantization = f->fmt.pix.quantization; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int coda_s_fmt_vid_out(struct file *file, void *priv, 90662306a36Sopenharmony_ci struct v4l2_format *f) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 90962306a36Sopenharmony_ci const struct coda_codec *codec; 91062306a36Sopenharmony_ci struct v4l2_format f_cap; 91162306a36Sopenharmony_ci struct vb2_queue *dst_vq; 91262306a36Sopenharmony_ci int ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ret = coda_try_fmt_vid_out(file, priv, f); 91562306a36Sopenharmony_ci if (ret) 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ret = coda_s_fmt(ctx, f, NULL); 91962306a36Sopenharmony_ci if (ret) 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ctx->colorspace = f->fmt.pix.colorspace; 92362306a36Sopenharmony_ci ctx->xfer_func = f->fmt.pix.xfer_func; 92462306a36Sopenharmony_ci ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; 92562306a36Sopenharmony_ci ctx->quantization = f->fmt.pix.quantization; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (ctx->inst_type != CODA_INST_DECODER) 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Setting the coded format determines the selected codec */ 93162306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, 93262306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420); 93362306a36Sopenharmony_ci if (!codec) { 93462306a36Sopenharmony_ci v4l2_err(&ctx->dev->v4l2_dev, "failed to determine codec\n"); 93562306a36Sopenharmony_ci return -EINVAL; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci ctx->codec = codec; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 94062306a36Sopenharmony_ci if (!dst_vq) 94162306a36Sopenharmony_ci return -EINVAL; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* 94462306a36Sopenharmony_ci * Setting the capture queue format is not possible while the capture 94562306a36Sopenharmony_ci * queue is still busy. This is not an error, but the user will have to 94662306a36Sopenharmony_ci * make sure themselves that the capture format is set correctly before 94762306a36Sopenharmony_ci * starting the output queue again. 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ci if (vb2_is_busy(dst_vq)) 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci memset(&f_cap, 0, sizeof(f_cap)); 95362306a36Sopenharmony_ci f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 95462306a36Sopenharmony_ci coda_g_fmt(file, priv, &f_cap); 95562306a36Sopenharmony_ci f_cap.fmt.pix.width = f->fmt.pix.width; 95662306a36Sopenharmony_ci f_cap.fmt.pix.height = f->fmt.pix.height; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return coda_s_fmt_vid_cap(file, priv, &f_cap); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int coda_reqbufs(struct file *file, void *priv, 96262306a36Sopenharmony_ci struct v4l2_requestbuffers *rb) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 96562306a36Sopenharmony_ci int ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ret = v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, rb); 96862306a36Sopenharmony_ci if (ret) 96962306a36Sopenharmony_ci return ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Allow to allocate instance specific per-context buffers, such as 97362306a36Sopenharmony_ci * bitstream ringbuffer, slice buffer, work buffer, etc. if needed. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci if (rb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ctx->ops->reqbufs) 97662306a36Sopenharmony_ci return ctx->ops->reqbufs(ctx, rb); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic int coda_qbuf(struct file *file, void *priv, 98262306a36Sopenharmony_ci struct v4l2_buffer *buf) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && 98762306a36Sopenharmony_ci buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 98862306a36Sopenharmony_ci buf->flags &= ~V4L2_BUF_FLAG_LAST; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(priv); 99662306a36Sopenharmony_ci int ret; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && 100162306a36Sopenharmony_ci buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 100262306a36Sopenharmony_ci buf->flags &= ~V4L2_BUF_FLAG_LAST; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci return ret; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_civoid coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, 100862306a36Sopenharmony_ci enum vb2_buffer_state state) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci const struct v4l2_event eos_event = { 101162306a36Sopenharmony_ci .type = V4L2_EVENT_EOS 101262306a36Sopenharmony_ci }; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (buf->flags & V4L2_BUF_FLAG_LAST) 101562306a36Sopenharmony_ci v4l2_event_queue_fh(&ctx->fh, &eos_event); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci v4l2_m2m_buf_done(buf, state); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int coda_g_selection(struct file *file, void *fh, 102162306a36Sopenharmony_ci struct v4l2_selection *s) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 102462306a36Sopenharmony_ci struct coda_q_data *q_data; 102562306a36Sopenharmony_ci struct v4l2_rect r, *rsel; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci q_data = get_q_data(ctx, s->type); 102862306a36Sopenharmony_ci if (!q_data) 102962306a36Sopenharmony_ci return -EINVAL; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci r.left = 0; 103262306a36Sopenharmony_ci r.top = 0; 103362306a36Sopenharmony_ci r.width = q_data->width; 103462306a36Sopenharmony_ci r.height = q_data->height; 103562306a36Sopenharmony_ci rsel = &q_data->rect; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci switch (s->target) { 103862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 103962306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 104062306a36Sopenharmony_ci rsel = &r; 104162306a36Sopenharmony_ci fallthrough; 104262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 104362306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 104462306a36Sopenharmony_ci ctx->inst_type == CODA_INST_DECODER) 104562306a36Sopenharmony_ci return -EINVAL; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 104862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_PADDED: 104962306a36Sopenharmony_ci rsel = &r; 105062306a36Sopenharmony_ci fallthrough; 105162306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 105262306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 105362306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 105462306a36Sopenharmony_ci ctx->inst_type == CODA_INST_ENCODER) 105562306a36Sopenharmony_ci return -EINVAL; 105662306a36Sopenharmony_ci break; 105762306a36Sopenharmony_ci default: 105862306a36Sopenharmony_ci return -EINVAL; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci s->r = *rsel; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic int coda_s_selection(struct file *file, void *fh, 106762306a36Sopenharmony_ci struct v4l2_selection *s) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 107062306a36Sopenharmony_ci struct coda_q_data *q_data; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci switch (s->target) { 107362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 107462306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_ENCODER && 107562306a36Sopenharmony_ci s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 107662306a36Sopenharmony_ci q_data = get_q_data(ctx, s->type); 107762306a36Sopenharmony_ci if (!q_data) 107862306a36Sopenharmony_ci return -EINVAL; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci s->r.left = 0; 108162306a36Sopenharmony_ci s->r.top = 0; 108262306a36Sopenharmony_ci s->r.width = clamp(s->r.width, 2U, q_data->width); 108362306a36Sopenharmony_ci s->r.height = clamp(s->r.height, 2U, q_data->height); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_LE) { 108662306a36Sopenharmony_ci s->r.width = round_up(s->r.width, 2); 108762306a36Sopenharmony_ci s->r.height = round_up(s->r.height, 2); 108862306a36Sopenharmony_ci } else { 108962306a36Sopenharmony_ci s->r.width = round_down(s->r.width, 2); 109062306a36Sopenharmony_ci s->r.height = round_down(s->r.height, 2); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci q_data->rect = s->r; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci coda_dbg(1, ctx, "Setting crop rectangle: %dx%d\n", 109662306a36Sopenharmony_ci s->r.width, s->r.height); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci fallthrough; 110162306a36Sopenharmony_ci case V4L2_SEL_TGT_NATIVE_SIZE: 110262306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 110362306a36Sopenharmony_ci return coda_g_selection(file, fh, s); 110462306a36Sopenharmony_ci default: 110562306a36Sopenharmony_ci /* v4l2-compliance expects this to fail for read-only targets */ 110662306a36Sopenharmony_ci return -EINVAL; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic void coda_wake_up_capture_queue(struct coda_ctx *ctx) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct vb2_queue *dst_vq; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci coda_dbg(1, ctx, "waking up capture queue\n"); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 111762306a36Sopenharmony_ci dst_vq->last_buffer_dequeued = true; 111862306a36Sopenharmony_ci wake_up(&dst_vq->done_wq); 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic int coda_encoder_cmd(struct file *file, void *fh, 112262306a36Sopenharmony_ci struct v4l2_encoder_cmd *ec) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 112562306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf; 112662306a36Sopenharmony_ci int ret; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); 112962306a36Sopenharmony_ci if (ret < 0) 113062306a36Sopenharmony_ci return ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci mutex_lock(&ctx->wakeup_mutex); 113362306a36Sopenharmony_ci buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); 113462306a36Sopenharmony_ci if (buf) { 113562306a36Sopenharmony_ci /* 113662306a36Sopenharmony_ci * If the last output buffer is still on the queue, make sure 113762306a36Sopenharmony_ci * that decoder finish_run will see the last flag and report it 113862306a36Sopenharmony_ci * to userspace. 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_ci buf->flags |= V4L2_BUF_FLAG_LAST; 114162306a36Sopenharmony_ci } else { 114262306a36Sopenharmony_ci /* Set the stream-end flag on this context */ 114362306a36Sopenharmony_ci ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * If the last output buffer has already been taken from the 114762306a36Sopenharmony_ci * queue, wake up the capture queue and signal end of stream 114862306a36Sopenharmony_ci * via the -EPIPE mechanism. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci coda_wake_up_capture_queue(ctx); 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci mutex_unlock(&ctx->wakeup_mutex); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic bool coda_mark_last_meta(struct coda_ctx *ctx) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct coda_buffer_meta *meta; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci coda_dbg(1, ctx, "marking last meta\n"); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci spin_lock(&ctx->buffer_meta_lock); 116462306a36Sopenharmony_ci if (list_empty(&ctx->buffer_meta_list)) { 116562306a36Sopenharmony_ci spin_unlock(&ctx->buffer_meta_lock); 116662306a36Sopenharmony_ci return false; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci meta = list_last_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, 117062306a36Sopenharmony_ci list); 117162306a36Sopenharmony_ci meta->last = true; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci spin_unlock(&ctx->buffer_meta_lock); 117462306a36Sopenharmony_ci return true; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic bool coda_mark_last_dst_buf(struct coda_ctx *ctx) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf; 118062306a36Sopenharmony_ci struct vb2_buffer *dst_vb; 118162306a36Sopenharmony_ci struct vb2_queue *dst_vq; 118262306a36Sopenharmony_ci unsigned long flags; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci coda_dbg(1, ctx, "marking last capture buffer\n"); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 118762306a36Sopenharmony_ci spin_lock_irqsave(&dst_vq->done_lock, flags); 118862306a36Sopenharmony_ci if (list_empty(&dst_vq->done_list)) { 118962306a36Sopenharmony_ci spin_unlock_irqrestore(&dst_vq->done_lock, flags); 119062306a36Sopenharmony_ci return false; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer, 119462306a36Sopenharmony_ci done_entry); 119562306a36Sopenharmony_ci buf = to_vb2_v4l2_buffer(dst_vb); 119662306a36Sopenharmony_ci buf->flags |= V4L2_BUF_FLAG_LAST; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci spin_unlock_irqrestore(&dst_vq->done_lock, flags); 119962306a36Sopenharmony_ci return true; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int coda_decoder_cmd(struct file *file, void *fh, 120362306a36Sopenharmony_ci struct v4l2_decoder_cmd *dc) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 120662306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 120762306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf; 120862306a36Sopenharmony_ci struct vb2_queue *dst_vq; 120962306a36Sopenharmony_ci bool stream_end; 121062306a36Sopenharmony_ci bool wakeup; 121162306a36Sopenharmony_ci int ret; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); 121462306a36Sopenharmony_ci if (ret < 0) 121562306a36Sopenharmony_ci return ret; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci switch (dc->cmd) { 121862306a36Sopenharmony_ci case V4L2_DEC_CMD_START: 121962306a36Sopenharmony_ci mutex_lock(&dev->coda_mutex); 122062306a36Sopenharmony_ci mutex_lock(&ctx->bitstream_mutex); 122162306a36Sopenharmony_ci coda_bitstream_flush(ctx); 122262306a36Sopenharmony_ci dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 122362306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE); 122462306a36Sopenharmony_ci vb2_clear_last_buffer_dequeued(dst_vq); 122562306a36Sopenharmony_ci ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; 122662306a36Sopenharmony_ci coda_fill_bitstream(ctx, NULL); 122762306a36Sopenharmony_ci mutex_unlock(&ctx->bitstream_mutex); 122862306a36Sopenharmony_ci mutex_unlock(&dev->coda_mutex); 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci case V4L2_DEC_CMD_STOP: 123162306a36Sopenharmony_ci stream_end = false; 123262306a36Sopenharmony_ci wakeup = false; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci mutex_lock(&ctx->wakeup_mutex); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); 123762306a36Sopenharmony_ci if (buf) { 123862306a36Sopenharmony_ci coda_dbg(1, ctx, "marking last pending buffer\n"); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* Mark last buffer */ 124162306a36Sopenharmony_ci buf->flags |= V4L2_BUF_FLAG_LAST; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { 124462306a36Sopenharmony_ci coda_dbg(1, ctx, "all remaining buffers queued\n"); 124562306a36Sopenharmony_ci stream_end = true; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci } else { 124862306a36Sopenharmony_ci if (ctx->use_bit) 124962306a36Sopenharmony_ci if (coda_mark_last_meta(ctx)) 125062306a36Sopenharmony_ci stream_end = true; 125162306a36Sopenharmony_ci else 125262306a36Sopenharmony_ci wakeup = true; 125362306a36Sopenharmony_ci else 125462306a36Sopenharmony_ci if (!coda_mark_last_dst_buf(ctx)) 125562306a36Sopenharmony_ci wakeup = true; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (stream_end) { 125962306a36Sopenharmony_ci coda_dbg(1, ctx, "all remaining buffers queued\n"); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* Set the stream-end flag on this context */ 126262306a36Sopenharmony_ci coda_bit_stream_end_flag(ctx); 126362306a36Sopenharmony_ci ctx->hold = false; 126462306a36Sopenharmony_ci v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (wakeup) { 126862306a36Sopenharmony_ci /* If there is no buffer in flight, wake up */ 126962306a36Sopenharmony_ci coda_wake_up_capture_queue(ctx); 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci mutex_unlock(&ctx->wakeup_mutex); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci default: 127562306a36Sopenharmony_ci return -EINVAL; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic int coda_enum_framesizes(struct file *file, void *fh, 128262306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 128562306a36Sopenharmony_ci struct coda_q_data *q_data_dst; 128662306a36Sopenharmony_ci const struct coda_codec *codec; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (fsize->index) 128962306a36Sopenharmony_ci return -EINVAL; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (coda_format_normalize_yuv(fsize->pixel_format) == 129262306a36Sopenharmony_ci V4L2_PIX_FMT_YUV420) { 129362306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 129462306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, fsize->pixel_format, 129562306a36Sopenharmony_ci q_data_dst->fourcc); 129662306a36Sopenharmony_ci } else { 129762306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, 129862306a36Sopenharmony_ci fsize->pixel_format); 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci if (!codec) 130162306a36Sopenharmony_ci return -EINVAL; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 130462306a36Sopenharmony_ci fsize->stepwise.min_width = MIN_W; 130562306a36Sopenharmony_ci fsize->stepwise.max_width = codec->max_w; 130662306a36Sopenharmony_ci fsize->stepwise.step_width = 1; 130762306a36Sopenharmony_ci fsize->stepwise.min_height = MIN_H; 130862306a36Sopenharmony_ci fsize->stepwise.max_height = codec->max_h; 130962306a36Sopenharmony_ci fsize->stepwise.step_height = 1; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci return 0; 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic int coda_enum_frameintervals(struct file *file, void *fh, 131562306a36Sopenharmony_ci struct v4l2_frmivalenum *f) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 131862306a36Sopenharmony_ci struct coda_q_data *q_data; 131962306a36Sopenharmony_ci const struct coda_codec *codec; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (f->index) 132262306a36Sopenharmony_ci return -EINVAL; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Disallow YUYV if the vdoa is not available */ 132562306a36Sopenharmony_ci if (!ctx->vdoa && f->pixel_format == V4L2_PIX_FMT_YUYV) 132662306a36Sopenharmony_ci return -EINVAL; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (coda_format_normalize_yuv(f->pixel_format) == V4L2_PIX_FMT_YUV420) { 132962306a36Sopenharmony_ci q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 133062306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, f->pixel_format, 133162306a36Sopenharmony_ci q_data->fourcc); 133262306a36Sopenharmony_ci } else { 133362306a36Sopenharmony_ci codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, 133462306a36Sopenharmony_ci f->pixel_format); 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci if (!codec) 133762306a36Sopenharmony_ci return -EINVAL; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (f->width < MIN_W || f->width > codec->max_w || 134062306a36Sopenharmony_ci f->height < MIN_H || f->height > codec->max_h) 134162306a36Sopenharmony_ci return -EINVAL; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; 134462306a36Sopenharmony_ci f->stepwise.min.numerator = 1; 134562306a36Sopenharmony_ci f->stepwise.min.denominator = 65535; 134662306a36Sopenharmony_ci f->stepwise.max.numerator = 65536; 134762306a36Sopenharmony_ci f->stepwise.max.denominator = 1; 134862306a36Sopenharmony_ci f->stepwise.step.numerator = 1; 134962306a36Sopenharmony_ci f->stepwise.step.denominator = 1; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci return 0; 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_cistatic int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 135762306a36Sopenharmony_ci struct v4l2_fract *tpf; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 136062306a36Sopenharmony_ci return -EINVAL; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 136362306a36Sopenharmony_ci tpf = &a->parm.output.timeperframe; 136462306a36Sopenharmony_ci tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; 136562306a36Sopenharmony_ci tpf->numerator = 1 + (ctx->params.framerate >> 136662306a36Sopenharmony_ci CODA_FRATE_DIV_OFFSET); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci/* 137262306a36Sopenharmony_ci * Approximate timeperframe v4l2_fract with values that can be written 137362306a36Sopenharmony_ci * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. 137462306a36Sopenharmony_ci */ 137562306a36Sopenharmony_cistatic void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct v4l2_fract s = *timeperframe; 137862306a36Sopenharmony_ci struct v4l2_fract f0; 137962306a36Sopenharmony_ci struct v4l2_fract f1 = { 1, 0 }; 138062306a36Sopenharmony_ci struct v4l2_fract f2 = { 0, 1 }; 138162306a36Sopenharmony_ci unsigned int i, div, s_denominator; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* Lower bound is 1/65535 */ 138462306a36Sopenharmony_ci if (s.numerator == 0 || s.denominator / s.numerator > 65535) { 138562306a36Sopenharmony_ci timeperframe->numerator = 1; 138662306a36Sopenharmony_ci timeperframe->denominator = 65535; 138762306a36Sopenharmony_ci return; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* Upper bound is 65536/1 */ 139162306a36Sopenharmony_ci if (s.denominator == 0 || s.numerator / s.denominator > 65536) { 139262306a36Sopenharmony_ci timeperframe->numerator = 65536; 139362306a36Sopenharmony_ci timeperframe->denominator = 1; 139462306a36Sopenharmony_ci return; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* Reduce fraction to lowest terms */ 139862306a36Sopenharmony_ci div = gcd(s.numerator, s.denominator); 139962306a36Sopenharmony_ci if (div > 1) { 140062306a36Sopenharmony_ci s.numerator /= div; 140162306a36Sopenharmony_ci s.denominator /= div; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (s.numerator <= 65536 && s.denominator < 65536) { 140562306a36Sopenharmony_ci *timeperframe = s; 140662306a36Sopenharmony_ci return; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* Find successive convergents from continued fraction expansion */ 141062306a36Sopenharmony_ci while (f2.numerator <= 65536 && f2.denominator < 65536) { 141162306a36Sopenharmony_ci f0 = f1; 141262306a36Sopenharmony_ci f1 = f2; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* Stop when f2 exactly equals timeperframe */ 141562306a36Sopenharmony_ci if (s.numerator == 0) 141662306a36Sopenharmony_ci break; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci i = s.denominator / s.numerator; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci f2.numerator = f0.numerator + i * f1.numerator; 142162306a36Sopenharmony_ci f2.denominator = f0.denominator + i * f2.denominator; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci s_denominator = s.numerator; 142462306a36Sopenharmony_ci s.numerator = s.denominator % s.numerator; 142562306a36Sopenharmony_ci s.denominator = s_denominator; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci *timeperframe = f1; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | 143462306a36Sopenharmony_ci timeperframe->denominator; 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 144062306a36Sopenharmony_ci struct v4l2_fract *tpf; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 144362306a36Sopenharmony_ci return -EINVAL; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 144662306a36Sopenharmony_ci tpf = &a->parm.output.timeperframe; 144762306a36Sopenharmony_ci coda_approximate_timeperframe(tpf); 144862306a36Sopenharmony_ci ctx->params.framerate = coda_timeperframe_to_frate(tpf); 144962306a36Sopenharmony_ci ctx->params.framerate_changed = true; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int coda_subscribe_event(struct v4l2_fh *fh, 145562306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(fh); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci switch (sub->type) { 146062306a36Sopenharmony_ci case V4L2_EVENT_EOS: 146162306a36Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 0, NULL); 146262306a36Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 146362306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER) 146462306a36Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 0, NULL); 146562306a36Sopenharmony_ci else 146662306a36Sopenharmony_ci return -EINVAL; 146762306a36Sopenharmony_ci default: 146862306a36Sopenharmony_ci return v4l2_ctrl_subscribe_event(fh, sub); 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops coda_ioctl_ops = { 147362306a36Sopenharmony_ci .vidioc_querycap = coda_querycap, 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = coda_enum_fmt, 147662306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = coda_g_fmt, 147762306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap, 147862306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap, 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = coda_enum_fmt, 148162306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = coda_g_fmt, 148262306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out, 148362306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out, 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci .vidioc_reqbufs = coda_reqbufs, 148662306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci .vidioc_qbuf = coda_qbuf, 148962306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 149062306a36Sopenharmony_ci .vidioc_dqbuf = coda_dqbuf, 149162306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 149262306a36Sopenharmony_ci .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 149562306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci .vidioc_g_selection = coda_g_selection, 149862306a36Sopenharmony_ci .vidioc_s_selection = coda_s_selection, 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, 150162306a36Sopenharmony_ci .vidioc_encoder_cmd = coda_encoder_cmd, 150262306a36Sopenharmony_ci .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, 150362306a36Sopenharmony_ci .vidioc_decoder_cmd = coda_decoder_cmd, 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci .vidioc_g_parm = coda_g_parm, 150662306a36Sopenharmony_ci .vidioc_s_parm = coda_s_parm, 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci .vidioc_enum_framesizes = coda_enum_framesizes, 150962306a36Sopenharmony_ci .vidioc_enum_frameintervals = coda_enum_frameintervals, 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci .vidioc_subscribe_event = coda_subscribe_event, 151262306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 151362306a36Sopenharmony_ci}; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci/* 151662306a36Sopenharmony_ci * Mem-to-mem operations. 151762306a36Sopenharmony_ci */ 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cistatic void coda_device_run(void *m2m_priv) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci struct coda_ctx *ctx = m2m_priv; 152262306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci queue_work(dev->workqueue, &ctx->pic_run_work); 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic void coda_pic_run_work(struct work_struct *work) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci struct coda_ctx *ctx = container_of(work, struct coda_ctx, pic_run_work); 153062306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 153162306a36Sopenharmony_ci int ret; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci mutex_lock(&ctx->buffer_mutex); 153462306a36Sopenharmony_ci mutex_lock(&dev->coda_mutex); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci ret = ctx->ops->prepare_run(ctx); 153762306a36Sopenharmony_ci if (ret < 0 && ctx->inst_type == CODA_INST_DECODER) 153862306a36Sopenharmony_ci goto out; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&ctx->completion, 154162306a36Sopenharmony_ci msecs_to_jiffies(1000))) { 154262306a36Sopenharmony_ci if (ctx->use_bit) { 154362306a36Sopenharmony_ci dev_err(dev->dev, "CODA PIC_RUN timeout\n"); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci ctx->hold = true; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci coda_hw_reset(ctx); 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (ctx->ops->run_timeout) 155162306a36Sopenharmony_ci ctx->ops->run_timeout(ctx); 155262306a36Sopenharmony_ci } else { 155362306a36Sopenharmony_ci ctx->ops->finish_run(ctx); 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && 155762306a36Sopenharmony_ci ctx->ops->seq_end_work) 155862306a36Sopenharmony_ci queue_work(dev->workqueue, &ctx->seq_end_work); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ciout: 156162306a36Sopenharmony_ci mutex_unlock(&dev->coda_mutex); 156262306a36Sopenharmony_ci mutex_unlock(&ctx->buffer_mutex); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic int coda_job_ready(void *m2m_priv) 156862306a36Sopenharmony_ci{ 156962306a36Sopenharmony_ci struct coda_ctx *ctx = m2m_priv; 157062306a36Sopenharmony_ci int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* 157362306a36Sopenharmony_ci * For both 'P' and 'key' frame cases 1 picture 157462306a36Sopenharmony_ci * and 1 frame are needed. In the decoder case, 157562306a36Sopenharmony_ci * the compressed frame can be in the bitstream. 157662306a36Sopenharmony_ci */ 157762306a36Sopenharmony_ci if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { 157862306a36Sopenharmony_ci coda_dbg(1, ctx, "not ready: not enough vid-out buffers.\n"); 157962306a36Sopenharmony_ci return 0; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { 158362306a36Sopenharmony_ci coda_dbg(1, ctx, "not ready: not enough vid-cap buffers.\n"); 158462306a36Sopenharmony_ci return 0; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { 158862306a36Sopenharmony_ci bool stream_end = ctx->bit_stream_param & 158962306a36Sopenharmony_ci CODA_BIT_STREAM_END_FLAG; 159062306a36Sopenharmony_ci int num_metas = ctx->num_metas; 159162306a36Sopenharmony_ci struct coda_buffer_meta *meta; 159262306a36Sopenharmony_ci unsigned int count; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci count = hweight32(ctx->frm_dis_flg); 159562306a36Sopenharmony_ci if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) { 159662306a36Sopenharmony_ci coda_dbg(1, ctx, 159762306a36Sopenharmony_ci "not ready: all internal buffers in use: %d/%d (0x%x)", 159862306a36Sopenharmony_ci count, ctx->num_internal_frames, 159962306a36Sopenharmony_ci ctx->frm_dis_flg); 160062306a36Sopenharmony_ci return 0; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (ctx->hold && !src_bufs) { 160462306a36Sopenharmony_ci coda_dbg(1, ctx, 160562306a36Sopenharmony_ci "not ready: on hold for more buffers.\n"); 160662306a36Sopenharmony_ci return 0; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (!stream_end && (num_metas + src_bufs) < 2) { 161062306a36Sopenharmony_ci coda_dbg(1, ctx, 161162306a36Sopenharmony_ci "not ready: need 2 buffers available (queue:%d + bitstream:%d)\n", 161262306a36Sopenharmony_ci num_metas, src_bufs); 161362306a36Sopenharmony_ci return 0; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci meta = list_first_entry(&ctx->buffer_meta_list, 161762306a36Sopenharmony_ci struct coda_buffer_meta, list); 161862306a36Sopenharmony_ci if (!coda_bitstream_can_fetch_past(ctx, meta->end) && 161962306a36Sopenharmony_ci !stream_end) { 162062306a36Sopenharmony_ci coda_dbg(1, ctx, 162162306a36Sopenharmony_ci "not ready: not enough bitstream data to read past %u (%u)\n", 162262306a36Sopenharmony_ci meta->end, ctx->bitstream_fifo.kfifo.in); 162362306a36Sopenharmony_ci return 0; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (ctx->aborting) { 162862306a36Sopenharmony_ci coda_dbg(1, ctx, "not ready: aborting\n"); 162962306a36Sopenharmony_ci return 0; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci coda_dbg(2, ctx, "job ready\n"); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci return 1; 163562306a36Sopenharmony_ci} 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_cistatic void coda_job_abort(void *priv) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct coda_ctx *ctx = priv; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci ctx->aborting = 1; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci coda_dbg(1, ctx, "job abort\n"); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic const struct v4l2_m2m_ops coda_m2m_ops = { 164762306a36Sopenharmony_ci .device_run = coda_device_run, 164862306a36Sopenharmony_ci .job_ready = coda_job_ready, 164962306a36Sopenharmony_ci .job_abort = coda_job_abort, 165062306a36Sopenharmony_ci}; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_cistatic void set_default_params(struct coda_ctx *ctx) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci unsigned int max_w, max_h, usize, csize; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], 165762306a36Sopenharmony_ci ctx->cvd->dst_formats[0]); 165862306a36Sopenharmony_ci max_w = min(ctx->codec->max_w, 1920U); 165962306a36Sopenharmony_ci max_h = min(ctx->codec->max_h, 1088U); 166062306a36Sopenharmony_ci usize = max_w * max_h * 3 / 2; 166162306a36Sopenharmony_ci csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci ctx->params.codec_mode = ctx->codec->mode; 166462306a36Sopenharmony_ci if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG || 166562306a36Sopenharmony_ci ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) { 166662306a36Sopenharmony_ci ctx->colorspace = V4L2_COLORSPACE_SRGB; 166762306a36Sopenharmony_ci ctx->xfer_func = V4L2_XFER_FUNC_SRGB; 166862306a36Sopenharmony_ci ctx->ycbcr_enc = V4L2_YCBCR_ENC_601; 166962306a36Sopenharmony_ci ctx->quantization = V4L2_QUANTIZATION_FULL_RANGE; 167062306a36Sopenharmony_ci } else { 167162306a36Sopenharmony_ci ctx->colorspace = V4L2_COLORSPACE_REC709; 167262306a36Sopenharmony_ci ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; 167362306a36Sopenharmony_ci ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 167462306a36Sopenharmony_ci ctx->quantization = V4L2_QUANTIZATION_DEFAULT; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci ctx->params.framerate = 30; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci /* Default formats for output and input queues */ 167962306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; 168062306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; 168162306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].width = max_w; 168262306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].height = max_h; 168362306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].width = max_w; 168462306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].height = max_h; 168562306a36Sopenharmony_ci if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { 168662306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; 168762306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; 168862306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].bytesperline = 0; 168962306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].sizeimage = csize; 169062306a36Sopenharmony_ci } else { 169162306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; 169262306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; 169362306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; 169462306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].sizeimage = usize; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; 169762306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; 169862306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].rect.width = max_w; 169962306a36Sopenharmony_ci ctx->q_data[V4L2_M2M_DST].rect.height = max_h; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* 170262306a36Sopenharmony_ci * Since the RBC2AXI logic only supports a single chroma plane, 170362306a36Sopenharmony_ci * macroblock tiling only works for to NV12 pixel format. 170462306a36Sopenharmony_ci */ 170562306a36Sopenharmony_ci ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci/* 170962306a36Sopenharmony_ci * Queue operations 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_cistatic int coda_queue_setup(struct vb2_queue *vq, 171262306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 171362306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct coda_ctx *ctx = vb2_get_drv_priv(vq); 171662306a36Sopenharmony_ci struct coda_q_data *q_data; 171762306a36Sopenharmony_ci unsigned int size; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci q_data = get_q_data(ctx, vq->type); 172062306a36Sopenharmony_ci size = q_data->sizeimage; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (*nplanes) 172362306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci *nplanes = 1; 172662306a36Sopenharmony_ci sizes[0] = size; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci coda_dbg(1, ctx, "get %d buffer(s) of size %d each.\n", *nbuffers, 172962306a36Sopenharmony_ci size); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci return 0; 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_cistatic int coda_buf_prepare(struct vb2_buffer *vb) 173562306a36Sopenharmony_ci{ 173662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 173762306a36Sopenharmony_ci struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 173862306a36Sopenharmony_ci struct coda_q_data *q_data; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci q_data = get_q_data(ctx, vb->vb2_queue->type); 174162306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { 174262306a36Sopenharmony_ci if (vbuf->field == V4L2_FIELD_ANY) 174362306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 174462306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_NONE) { 174562306a36Sopenharmony_ci v4l2_warn(&ctx->dev->v4l2_dev, 174662306a36Sopenharmony_ci "%s field isn't supported\n", __func__); 174762306a36Sopenharmony_ci return -EINVAL; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < q_data->sizeimage) { 175262306a36Sopenharmony_ci v4l2_warn(&ctx->dev->v4l2_dev, 175362306a36Sopenharmony_ci "%s data will not fit into plane (%lu < %lu)\n", 175462306a36Sopenharmony_ci __func__, vb2_plane_size(vb, 0), 175562306a36Sopenharmony_ci (long)q_data->sizeimage); 175662306a36Sopenharmony_ci return -EINVAL; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci return 0; 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci if (!ctrl) 176562306a36Sopenharmony_ci return; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci v4l2_ctrl_lock(ctrl); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* 177062306a36Sopenharmony_ci * Extend the control range if the parsed stream contains a known but 177162306a36Sopenharmony_ci * unsupported value or level. 177262306a36Sopenharmony_ci */ 177362306a36Sopenharmony_ci if (value > ctrl->maximum) { 177462306a36Sopenharmony_ci __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, 177562306a36Sopenharmony_ci ctrl->menu_skip_mask & ~(1 << value), 177662306a36Sopenharmony_ci ctrl->default_value); 177762306a36Sopenharmony_ci } else if (value < ctrl->minimum) { 177862306a36Sopenharmony_ci __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, 177962306a36Sopenharmony_ci ctrl->menu_skip_mask & ~(1 << value), 178062306a36Sopenharmony_ci ctrl->default_value); 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci __v4l2_ctrl_s_ctrl(ctrl, value); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci v4l2_ctrl_unlock(ctrl); 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_civoid coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, 178962306a36Sopenharmony_ci u8 level_idc) 179062306a36Sopenharmony_ci{ 179162306a36Sopenharmony_ci const char * const *profile_names; 179262306a36Sopenharmony_ci const char * const *level_names; 179362306a36Sopenharmony_ci struct v4l2_ctrl *profile_ctrl; 179462306a36Sopenharmony_ci struct v4l2_ctrl *level_ctrl; 179562306a36Sopenharmony_ci const char *codec_name; 179662306a36Sopenharmony_ci u32 profile_cid; 179762306a36Sopenharmony_ci u32 level_cid; 179862306a36Sopenharmony_ci int profile; 179962306a36Sopenharmony_ci int level; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci switch (ctx->codec->src_fourcc) { 180262306a36Sopenharmony_ci case V4L2_PIX_FMT_H264: 180362306a36Sopenharmony_ci codec_name = "H264"; 180462306a36Sopenharmony_ci profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; 180562306a36Sopenharmony_ci level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; 180662306a36Sopenharmony_ci profile_ctrl = ctx->h264_profile_ctrl; 180762306a36Sopenharmony_ci level_ctrl = ctx->h264_level_ctrl; 180862306a36Sopenharmony_ci profile = coda_h264_profile(profile_idc); 180962306a36Sopenharmony_ci level = coda_h264_level(level_idc); 181062306a36Sopenharmony_ci break; 181162306a36Sopenharmony_ci case V4L2_PIX_FMT_MPEG2: 181262306a36Sopenharmony_ci codec_name = "MPEG-2"; 181362306a36Sopenharmony_ci profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; 181462306a36Sopenharmony_ci level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; 181562306a36Sopenharmony_ci profile_ctrl = ctx->mpeg2_profile_ctrl; 181662306a36Sopenharmony_ci level_ctrl = ctx->mpeg2_level_ctrl; 181762306a36Sopenharmony_ci profile = coda_mpeg2_profile(profile_idc); 181862306a36Sopenharmony_ci level = coda_mpeg2_level(level_idc); 181962306a36Sopenharmony_ci break; 182062306a36Sopenharmony_ci case V4L2_PIX_FMT_MPEG4: 182162306a36Sopenharmony_ci codec_name = "MPEG-4"; 182262306a36Sopenharmony_ci profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; 182362306a36Sopenharmony_ci level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; 182462306a36Sopenharmony_ci profile_ctrl = ctx->mpeg4_profile_ctrl; 182562306a36Sopenharmony_ci level_ctrl = ctx->mpeg4_level_ctrl; 182662306a36Sopenharmony_ci profile = coda_mpeg4_profile(profile_idc); 182762306a36Sopenharmony_ci level = coda_mpeg4_level(level_idc); 182862306a36Sopenharmony_ci break; 182962306a36Sopenharmony_ci default: 183062306a36Sopenharmony_ci return; 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci profile_names = v4l2_ctrl_get_menu(profile_cid); 183462306a36Sopenharmony_ci level_names = v4l2_ctrl_get_menu(level_cid); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (profile < 0) { 183762306a36Sopenharmony_ci v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", 183862306a36Sopenharmony_ci codec_name, profile_idc); 183962306a36Sopenharmony_ci } else { 184062306a36Sopenharmony_ci coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, 184162306a36Sopenharmony_ci profile_names[profile]); 184262306a36Sopenharmony_ci coda_update_menu_ctrl(profile_ctrl, profile); 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci if (level < 0) { 184662306a36Sopenharmony_ci v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", 184762306a36Sopenharmony_ci codec_name, level_idc); 184862306a36Sopenharmony_ci } else { 184962306a36Sopenharmony_ci coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, 185062306a36Sopenharmony_ci level_names[level]); 185162306a36Sopenharmony_ci coda_update_menu_ctrl(level_ctrl, level); 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cistatic void coda_queue_source_change_event(struct coda_ctx *ctx) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci static const struct v4l2_event source_change_event = { 185862306a36Sopenharmony_ci .type = V4L2_EVENT_SOURCE_CHANGE, 185962306a36Sopenharmony_ci .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 186062306a36Sopenharmony_ci }; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci v4l2_event_queue_fh(&ctx->fh, &source_change_event); 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic void coda_buf_queue(struct vb2_buffer *vb) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 186862306a36Sopenharmony_ci struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 186962306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 187062306a36Sopenharmony_ci struct coda_q_data *q_data; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci q_data = get_q_data(ctx, vb->vb2_queue->type); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* 187562306a36Sopenharmony_ci * In the decoder case, immediately try to copy the buffer into the 187662306a36Sopenharmony_ci * bitstream ringbuffer and mark it as ready to be dequeued. 187762306a36Sopenharmony_ci */ 187862306a36Sopenharmony_ci if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 187962306a36Sopenharmony_ci /* 188062306a36Sopenharmony_ci * For backwards compatibility, queuing an empty buffer marks 188162306a36Sopenharmony_ci * the stream end 188262306a36Sopenharmony_ci */ 188362306a36Sopenharmony_ci if (vb2_get_plane_payload(vb, 0) == 0) 188462306a36Sopenharmony_ci coda_bit_stream_end_flag(ctx); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci if (q_data->fourcc == V4L2_PIX_FMT_H264) { 188762306a36Sopenharmony_ci /* 188862306a36Sopenharmony_ci * Unless already done, try to obtain profile_idc and 188962306a36Sopenharmony_ci * level_idc from the SPS header. This allows to decide 189062306a36Sopenharmony_ci * whether to enable reordering during sequence 189162306a36Sopenharmony_ci * initialization. 189262306a36Sopenharmony_ci */ 189362306a36Sopenharmony_ci if (!ctx->params.h264_profile_idc) { 189462306a36Sopenharmony_ci coda_sps_parse_profile(ctx, vb); 189562306a36Sopenharmony_ci coda_update_profile_level_ctrls(ctx, 189662306a36Sopenharmony_ci ctx->params.h264_profile_idc, 189762306a36Sopenharmony_ci ctx->params.h264_level_idc); 189862306a36Sopenharmony_ci } 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci mutex_lock(&ctx->bitstream_mutex); 190262306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 190362306a36Sopenharmony_ci if (vb2_is_streaming(vb->vb2_queue)) 190462306a36Sopenharmony_ci /* This set buf->sequence = ctx->qsequence++ */ 190562306a36Sopenharmony_ci coda_fill_bitstream(ctx, NULL); 190662306a36Sopenharmony_ci mutex_unlock(&ctx->bitstream_mutex); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if (!ctx->initialized) { 190962306a36Sopenharmony_ci /* 191062306a36Sopenharmony_ci * Run sequence initialization in case the queued 191162306a36Sopenharmony_ci * buffer contained headers. 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_ci if (vb2_is_streaming(vb->vb2_queue) && 191462306a36Sopenharmony_ci ctx->ops->seq_init_work) { 191562306a36Sopenharmony_ci queue_work(ctx->dev->workqueue, 191662306a36Sopenharmony_ci &ctx->seq_init_work); 191762306a36Sopenharmony_ci flush_work(&ctx->seq_init_work); 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci if (ctx->initialized) 192162306a36Sopenharmony_ci coda_queue_source_change_event(ctx); 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci } else { 192462306a36Sopenharmony_ci if ((ctx->inst_type == CODA_INST_ENCODER || !ctx->use_bit) && 192562306a36Sopenharmony_ci vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 192662306a36Sopenharmony_ci vbuf->sequence = ctx->qsequence++; 192762306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ciint coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, 193262306a36Sopenharmony_ci size_t size, const char *name, struct dentry *parent) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, 193562306a36Sopenharmony_ci GFP_KERNEL); 193662306a36Sopenharmony_ci if (!buf->vaddr) { 193762306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 193862306a36Sopenharmony_ci "Failed to allocate %s buffer of size %zu\n", 193962306a36Sopenharmony_ci name, size); 194062306a36Sopenharmony_ci return -ENOMEM; 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci buf->size = size; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci if (name && parent) { 194662306a36Sopenharmony_ci buf->blob.data = buf->vaddr; 194762306a36Sopenharmony_ci buf->blob.size = size; 194862306a36Sopenharmony_ci buf->dentry = debugfs_create_blob(name, 0444, parent, 194962306a36Sopenharmony_ci &buf->blob); 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci return 0; 195362306a36Sopenharmony_ci} 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_civoid coda_free_aux_buf(struct coda_dev *dev, 195662306a36Sopenharmony_ci struct coda_aux_buf *buf) 195762306a36Sopenharmony_ci{ 195862306a36Sopenharmony_ci if (buf->vaddr) { 195962306a36Sopenharmony_ci dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); 196062306a36Sopenharmony_ci buf->vaddr = NULL; 196162306a36Sopenharmony_ci buf->size = 0; 196262306a36Sopenharmony_ci debugfs_remove(buf->dentry); 196362306a36Sopenharmony_ci buf->dentry = NULL; 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_cistatic int coda_start_streaming(struct vb2_queue *q, unsigned int count) 196862306a36Sopenharmony_ci{ 196962306a36Sopenharmony_ci struct coda_ctx *ctx = vb2_get_drv_priv(q); 197062306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; 197162306a36Sopenharmony_ci struct coda_q_data *q_data_src, *q_data_dst; 197262306a36Sopenharmony_ci struct v4l2_m2m_buffer *m2m_buf, *tmp; 197362306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf; 197462306a36Sopenharmony_ci struct list_head list; 197562306a36Sopenharmony_ci int ret = 0; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (count < 1) 197862306a36Sopenharmony_ci return -EINVAL; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci coda_dbg(1, ctx, "start streaming %s\n", v4l2_type_names[q->type]); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); 198562306a36Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 198662306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { 198762306a36Sopenharmony_ci /* copy the buffers that were queued before streamon */ 198862306a36Sopenharmony_ci mutex_lock(&ctx->bitstream_mutex); 198962306a36Sopenharmony_ci coda_fill_bitstream(ctx, &list); 199062306a36Sopenharmony_ci mutex_unlock(&ctx->bitstream_mutex); 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci if (ctx->dev->devtype->product != CODA_960 && 199362306a36Sopenharmony_ci coda_get_bitstream_payload(ctx) < 512) { 199462306a36Sopenharmony_ci v4l2_err(v4l2_dev, "start payload < 512\n"); 199562306a36Sopenharmony_ci ret = -EINVAL; 199662306a36Sopenharmony_ci goto err; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (!ctx->initialized) { 200062306a36Sopenharmony_ci /* Run sequence initialization */ 200162306a36Sopenharmony_ci if (ctx->ops->seq_init_work) { 200262306a36Sopenharmony_ci queue_work(ctx->dev->workqueue, 200362306a36Sopenharmony_ci &ctx->seq_init_work); 200462306a36Sopenharmony_ci flush_work(&ctx->seq_init_work); 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci /* 201062306a36Sopenharmony_ci * Check the first input JPEG buffer to determine chroma 201162306a36Sopenharmony_ci * subsampling. 201262306a36Sopenharmony_ci */ 201362306a36Sopenharmony_ci if (q_data_src->fourcc == V4L2_PIX_FMT_JPEG) { 201462306a36Sopenharmony_ci buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 201562306a36Sopenharmony_ci coda_jpeg_decode_header(ctx, &buf->vb2_buf); 201662306a36Sopenharmony_ci /* 201762306a36Sopenharmony_ci * We have to start streaming even if the first buffer 201862306a36Sopenharmony_ci * does not contain a valid JPEG image. The error will 201962306a36Sopenharmony_ci * be caught during device run and will be signalled 202062306a36Sopenharmony_ci * via the capture buffer error flag. 202162306a36Sopenharmony_ci */ 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 202462306a36Sopenharmony_ci q_data_dst->width = round_up(q_data_src->width, 16); 202562306a36Sopenharmony_ci q_data_dst->height = round_up(q_data_src->height, 16); 202662306a36Sopenharmony_ci q_data_dst->bytesperline = q_data_dst->width; 202762306a36Sopenharmony_ci if (ctx->params.jpeg_chroma_subsampling == 202862306a36Sopenharmony_ci V4L2_JPEG_CHROMA_SUBSAMPLING_420) { 202962306a36Sopenharmony_ci q_data_dst->sizeimage = 203062306a36Sopenharmony_ci q_data_dst->bytesperline * 203162306a36Sopenharmony_ci q_data_dst->height * 3 / 2; 203262306a36Sopenharmony_ci if (q_data_dst->fourcc != V4L2_PIX_FMT_YUV420) 203362306a36Sopenharmony_ci q_data_dst->fourcc = V4L2_PIX_FMT_NV12; 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ci q_data_dst->sizeimage = 203662306a36Sopenharmony_ci q_data_dst->bytesperline * 203762306a36Sopenharmony_ci q_data_dst->height * 2; 203862306a36Sopenharmony_ci q_data_dst->fourcc = V4L2_PIX_FMT_YUV422P; 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci q_data_dst->rect.left = 0; 204162306a36Sopenharmony_ci q_data_dst->rect.top = 0; 204262306a36Sopenharmony_ci q_data_dst->rect.width = q_data_src->width; 204362306a36Sopenharmony_ci q_data_dst->rect.height = q_data_src->height; 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci ctx->streamon_out = 1; 204662306a36Sopenharmony_ci } else { 204762306a36Sopenharmony_ci ctx->streamon_cap = 1; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* Don't start the coda unless both queues are on */ 205162306a36Sopenharmony_ci if (!(ctx->streamon_out && ctx->streamon_cap)) 205262306a36Sopenharmony_ci goto out; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); 205562306a36Sopenharmony_ci if ((q_data_src->rect.width != q_data_dst->width && 205662306a36Sopenharmony_ci round_up(q_data_src->rect.width, 16) != q_data_dst->width) || 205762306a36Sopenharmony_ci (q_data_src->rect.height != q_data_dst->height && 205862306a36Sopenharmony_ci round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { 205962306a36Sopenharmony_ci v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", 206062306a36Sopenharmony_ci q_data_src->rect.width, q_data_src->rect.height, 206162306a36Sopenharmony_ci q_data_dst->width, q_data_dst->height); 206262306a36Sopenharmony_ci ret = -EINVAL; 206362306a36Sopenharmony_ci goto err; 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci /* Allow BIT decoder device_run with no new buffers queued */ 206762306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) 206862306a36Sopenharmony_ci v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci ctx->gopcounter = ctx->params.gop_size - 1; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) 207362306a36Sopenharmony_ci ctx->params.gop_size = 1; 207462306a36Sopenharmony_ci ctx->gopcounter = ctx->params.gop_size - 1; 207562306a36Sopenharmony_ci /* Only decoders have this control */ 207662306a36Sopenharmony_ci if (ctx->mb_err_cnt_ctrl) 207762306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci ret = ctx->ops->start_streaming(ctx); 208062306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER) { 208162306a36Sopenharmony_ci if (ret == -EAGAIN) 208262306a36Sopenharmony_ci goto out; 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci if (ret < 0) 208562306a36Sopenharmony_ci goto err; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ciout: 208862306a36Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 208962306a36Sopenharmony_ci list_for_each_entry_safe(m2m_buf, tmp, &list, list) { 209062306a36Sopenharmony_ci list_del(&m2m_buf->list); 209162306a36Sopenharmony_ci v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_DONE); 209262306a36Sopenharmony_ci } 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci return 0; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_cierr: 209762306a36Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 209862306a36Sopenharmony_ci list_for_each_entry_safe(m2m_buf, tmp, &list, list) { 209962306a36Sopenharmony_ci list_del(&m2m_buf->list); 210062306a36Sopenharmony_ci v4l2_m2m_buf_done(&m2m_buf->vb, VB2_BUF_STATE_QUEUED); 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) 210362306a36Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 210462306a36Sopenharmony_ci } else { 210562306a36Sopenharmony_ci while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) 210662306a36Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci return ret; 210962306a36Sopenharmony_ci} 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_cistatic void coda_stop_streaming(struct vb2_queue *q) 211262306a36Sopenharmony_ci{ 211362306a36Sopenharmony_ci struct coda_ctx *ctx = vb2_get_drv_priv(q); 211462306a36Sopenharmony_ci struct coda_dev *dev = ctx->dev; 211562306a36Sopenharmony_ci struct vb2_v4l2_buffer *buf; 211662306a36Sopenharmony_ci bool stop; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci stop = ctx->streamon_out && ctx->streamon_cap; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci coda_dbg(1, ctx, "stop streaming %s\n", v4l2_type_names[q->type]); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 212362306a36Sopenharmony_ci ctx->streamon_out = 0; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci coda_bit_stream_end_flag(ctx); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci ctx->qsequence = 0; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) 213062306a36Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); 213162306a36Sopenharmony_ci } else { 213262306a36Sopenharmony_ci ctx->streamon_cap = 0; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci ctx->osequence = 0; 213562306a36Sopenharmony_ci ctx->sequence_offset = 0; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) 213862306a36Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci if (stop) { 214262306a36Sopenharmony_ci struct coda_buffer_meta *meta; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (ctx->ops->seq_end_work) { 214562306a36Sopenharmony_ci queue_work(dev->workqueue, &ctx->seq_end_work); 214662306a36Sopenharmony_ci flush_work(&ctx->seq_end_work); 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci spin_lock(&ctx->buffer_meta_lock); 214962306a36Sopenharmony_ci while (!list_empty(&ctx->buffer_meta_list)) { 215062306a36Sopenharmony_ci meta = list_first_entry(&ctx->buffer_meta_list, 215162306a36Sopenharmony_ci struct coda_buffer_meta, list); 215262306a36Sopenharmony_ci list_del(&meta->list); 215362306a36Sopenharmony_ci kfree(meta); 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci ctx->num_metas = 0; 215662306a36Sopenharmony_ci spin_unlock(&ctx->buffer_meta_lock); 215762306a36Sopenharmony_ci kfifo_init(&ctx->bitstream_fifo, 215862306a36Sopenharmony_ci ctx->bitstream.vaddr, ctx->bitstream.size); 215962306a36Sopenharmony_ci ctx->runcounter = 0; 216062306a36Sopenharmony_ci ctx->aborting = 0; 216162306a36Sopenharmony_ci ctx->hold = false; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (!ctx->streamon_out && !ctx->streamon_cap) 216562306a36Sopenharmony_ci ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cistatic const struct vb2_ops coda_qops = { 216962306a36Sopenharmony_ci .queue_setup = coda_queue_setup, 217062306a36Sopenharmony_ci .buf_prepare = coda_buf_prepare, 217162306a36Sopenharmony_ci .buf_queue = coda_buf_queue, 217262306a36Sopenharmony_ci .start_streaming = coda_start_streaming, 217362306a36Sopenharmony_ci .stop_streaming = coda_stop_streaming, 217462306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 217562306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 217662306a36Sopenharmony_ci}; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_cistatic int coda_s_ctrl(struct v4l2_ctrl *ctrl) 217962306a36Sopenharmony_ci{ 218062306a36Sopenharmony_ci const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); 218162306a36Sopenharmony_ci struct coda_ctx *ctx = 218262306a36Sopenharmony_ci container_of(ctrl->handler, struct coda_ctx, ctrls); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci if (val_names) 218562306a36Sopenharmony_ci coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", 218662306a36Sopenharmony_ci ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); 218762306a36Sopenharmony_ci else 218862306a36Sopenharmony_ci coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", 218962306a36Sopenharmony_ci ctrl->id, ctrl->name, ctrl->val); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci switch (ctrl->id) { 219262306a36Sopenharmony_ci case V4L2_CID_HFLIP: 219362306a36Sopenharmony_ci if (ctrl->val) 219462306a36Sopenharmony_ci ctx->params.rot_mode |= CODA_MIR_HOR; 219562306a36Sopenharmony_ci else 219662306a36Sopenharmony_ci ctx->params.rot_mode &= ~CODA_MIR_HOR; 219762306a36Sopenharmony_ci break; 219862306a36Sopenharmony_ci case V4L2_CID_VFLIP: 219962306a36Sopenharmony_ci if (ctrl->val) 220062306a36Sopenharmony_ci ctx->params.rot_mode |= CODA_MIR_VER; 220162306a36Sopenharmony_ci else 220262306a36Sopenharmony_ci ctx->params.rot_mode &= ~CODA_MIR_VER; 220362306a36Sopenharmony_ci break; 220462306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_BITRATE: 220562306a36Sopenharmony_ci ctx->params.bitrate = ctrl->val / 1000; 220662306a36Sopenharmony_ci ctx->params.bitrate_changed = true; 220762306a36Sopenharmony_ci break; 220862306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_GOP_SIZE: 220962306a36Sopenharmony_ci ctx->params.gop_size = ctrl->val; 221062306a36Sopenharmony_ci break; 221162306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: 221262306a36Sopenharmony_ci ctx->params.h264_intra_qp = ctrl->val; 221362306a36Sopenharmony_ci ctx->params.h264_intra_qp_changed = true; 221462306a36Sopenharmony_ci break; 221562306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: 221662306a36Sopenharmony_ci ctx->params.h264_inter_qp = ctrl->val; 221762306a36Sopenharmony_ci break; 221862306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: 221962306a36Sopenharmony_ci ctx->params.h264_min_qp = ctrl->val; 222062306a36Sopenharmony_ci break; 222162306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: 222262306a36Sopenharmony_ci ctx->params.h264_max_qp = ctrl->val; 222362306a36Sopenharmony_ci break; 222462306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: 222562306a36Sopenharmony_ci ctx->params.h264_slice_alpha_c0_offset_div2 = ctrl->val; 222662306a36Sopenharmony_ci break; 222762306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: 222862306a36Sopenharmony_ci ctx->params.h264_slice_beta_offset_div2 = ctrl->val; 222962306a36Sopenharmony_ci break; 223062306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: 223162306a36Sopenharmony_ci ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; 223262306a36Sopenharmony_ci break; 223362306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: 223462306a36Sopenharmony_ci ctx->params.h264_constrained_intra_pred_flag = ctrl->val; 223562306a36Sopenharmony_ci break; 223662306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: 223762306a36Sopenharmony_ci ctx->params.frame_rc_enable = ctrl->val; 223862306a36Sopenharmony_ci break; 223962306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: 224062306a36Sopenharmony_ci ctx->params.mb_rc_enable = ctrl->val; 224162306a36Sopenharmony_ci break; 224262306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: 224362306a36Sopenharmony_ci ctx->params.h264_chroma_qp_index_offset = ctrl->val; 224462306a36Sopenharmony_ci break; 224562306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 224662306a36Sopenharmony_ci /* TODO: switch between baseline and constrained baseline */ 224762306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_ENCODER) 224862306a36Sopenharmony_ci ctx->params.h264_profile_idc = 66; 224962306a36Sopenharmony_ci break; 225062306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_LEVEL: 225162306a36Sopenharmony_ci /* nothing to do, this is set by the encoder */ 225262306a36Sopenharmony_ci break; 225362306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: 225462306a36Sopenharmony_ci ctx->params.mpeg4_intra_qp = ctrl->val; 225562306a36Sopenharmony_ci break; 225662306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: 225762306a36Sopenharmony_ci ctx->params.mpeg4_inter_qp = ctrl->val; 225862306a36Sopenharmony_ci break; 225962306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: 226062306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: 226162306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: 226262306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: 226362306a36Sopenharmony_ci /* nothing to do, these are fixed */ 226462306a36Sopenharmony_ci break; 226562306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: 226662306a36Sopenharmony_ci ctx->params.slice_mode = ctrl->val; 226762306a36Sopenharmony_ci ctx->params.slice_mode_changed = true; 226862306a36Sopenharmony_ci break; 226962306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: 227062306a36Sopenharmony_ci ctx->params.slice_max_mb = ctrl->val; 227162306a36Sopenharmony_ci ctx->params.slice_mode_changed = true; 227262306a36Sopenharmony_ci break; 227362306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: 227462306a36Sopenharmony_ci ctx->params.slice_max_bits = ctrl->val * 8; 227562306a36Sopenharmony_ci ctx->params.slice_mode_changed = true; 227662306a36Sopenharmony_ci break; 227762306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_HEADER_MODE: 227862306a36Sopenharmony_ci break; 227962306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: 228062306a36Sopenharmony_ci ctx->params.intra_refresh = ctrl->val; 228162306a36Sopenharmony_ci ctx->params.intra_refresh_changed = true; 228262306a36Sopenharmony_ci break; 228362306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: 228462306a36Sopenharmony_ci ctx->params.force_ipicture = true; 228562306a36Sopenharmony_ci break; 228662306a36Sopenharmony_ci case V4L2_CID_JPEG_COMPRESSION_QUALITY: 228762306a36Sopenharmony_ci coda_set_jpeg_compression_quality(ctx, ctrl->val); 228862306a36Sopenharmony_ci break; 228962306a36Sopenharmony_ci case V4L2_CID_JPEG_RESTART_INTERVAL: 229062306a36Sopenharmony_ci ctx->params.jpeg_restart_interval = ctrl->val; 229162306a36Sopenharmony_ci break; 229262306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_VBV_DELAY: 229362306a36Sopenharmony_ci ctx->params.vbv_delay = ctrl->val; 229462306a36Sopenharmony_ci break; 229562306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_VBV_SIZE: 229662306a36Sopenharmony_ci ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); 229762306a36Sopenharmony_ci break; 229862306a36Sopenharmony_ci default: 229962306a36Sopenharmony_ci coda_dbg(1, ctx, "Invalid control, id=%d, val=%d\n", 230062306a36Sopenharmony_ci ctrl->id, ctrl->val); 230162306a36Sopenharmony_ci return -EINVAL; 230262306a36Sopenharmony_ci } 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci return 0; 230562306a36Sopenharmony_ci} 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops coda_ctrl_ops = { 230862306a36Sopenharmony_ci .s_ctrl = coda_s_ctrl, 230962306a36Sopenharmony_ci}; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_cistatic void coda_encode_ctrls(struct coda_ctx *ctx) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci int max_gop_size = (ctx->dev->devtype->product == CODA_DX6) ? 60 : 99; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 231662306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1000, 0); 231762306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 231862306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, max_gop_size, 1, 16); 231962306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 232062306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); 232162306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 232262306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); 232362306a36Sopenharmony_ci if (ctx->dev->devtype->product != CODA_960) { 232462306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 232562306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 0, 51, 1, 12); 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 232862306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 0, 51, 1, 51); 232962306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 233062306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0); 233162306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 233262306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0); 233362306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 233462306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, 233562306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 233662306a36Sopenharmony_ci 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); 233762306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 233862306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 233962306a36Sopenharmony_ci 0); 234062306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 234162306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); 234262306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 234362306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, 0, 1, 1, 1); 234462306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 234562306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); 234662306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 234762306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_PROFILE, 234862306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, 0x0, 234962306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE); 235062306a36Sopenharmony_ci if (ctx->dev->devtype->product == CODA_HX4 || 235162306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_7541) { 235262306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 235362306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_LEVEL, 235462306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 235562306a36Sopenharmony_ci ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | 235662306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | 235762306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1)), 235862306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_LEVEL_3_1); 235962306a36Sopenharmony_ci } 236062306a36Sopenharmony_ci if (ctx->dev->devtype->product == CODA_960) { 236162306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 236262306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_LEVEL, 236362306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 236462306a36Sopenharmony_ci ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | 236562306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | 236662306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | 236762306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | 236862306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | 236962306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | 237062306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | 237162306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2)), 237262306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_LEVEL_4_0); 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 237562306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); 237662306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 237762306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); 237862306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 237962306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, 238062306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0, 238162306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE); 238262306a36Sopenharmony_ci if (ctx->dev->devtype->product == CODA_HX4 || 238362306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_7541 || 238462306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_960) { 238562306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 238662306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, 238762306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 238862306a36Sopenharmony_ci ~(1 << V4L2_MPEG_VIDEO_MPEG4_LEVEL_5), 238962306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); 239062306a36Sopenharmony_ci } 239162306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 239262306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, 239362306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, 239462306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); 239562306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 239662306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); 239762306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 239862306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 239962306a36Sopenharmony_ci 500); 240062306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, 240162306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_HEADER_MODE, 240262306a36Sopenharmony_ci V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, 240362306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), 240462306a36Sopenharmony_ci V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); 240562306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 240662306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 240762306a36Sopenharmony_ci 1920 * 1088 / 256, 1, 0); 240862306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 240962306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); 241062306a36Sopenharmony_ci /* 241162306a36Sopenharmony_ci * The maximum VBV size value is 0x7fffffff bits, 241262306a36Sopenharmony_ci * one bit less than 262144 KiB 241362306a36Sopenharmony_ci */ 241462306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 241562306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); 241662306a36Sopenharmony_ci} 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_cistatic void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) 241962306a36Sopenharmony_ci{ 242062306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 242162306a36Sopenharmony_ci V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); 242262306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 242362306a36Sopenharmony_ci V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); 242462306a36Sopenharmony_ci} 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_cistatic void coda_decode_ctrls(struct coda_ctx *ctx) 242762306a36Sopenharmony_ci{ 242862306a36Sopenharmony_ci u8 max; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 243162306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, 243262306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 243362306a36Sopenharmony_ci ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | 243462306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | 243562306a36Sopenharmony_ci (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), 243662306a36Sopenharmony_ci V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); 243762306a36Sopenharmony_ci if (ctx->h264_profile_ctrl) 243862306a36Sopenharmony_ci ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci if (ctx->dev->devtype->product == CODA_HX4 || 244162306a36Sopenharmony_ci ctx->dev->devtype->product == CODA_7541) 244262306a36Sopenharmony_ci max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; 244362306a36Sopenharmony_ci else if (ctx->dev->devtype->product == CODA_960) 244462306a36Sopenharmony_ci max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; 244562306a36Sopenharmony_ci else 244662306a36Sopenharmony_ci return; 244762306a36Sopenharmony_ci ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 244862306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); 244962306a36Sopenharmony_ci if (ctx->h264_level_ctrl) 245062306a36Sopenharmony_ci ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 245362306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, 245462306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, 245562306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); 245662306a36Sopenharmony_ci if (ctx->mpeg2_profile_ctrl) 245762306a36Sopenharmony_ci ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 246062306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, 246162306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, 246262306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); 246362306a36Sopenharmony_ci if (ctx->mpeg2_level_ctrl) 246462306a36Sopenharmony_ci ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 246762306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, 246862306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, 246962306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); 247062306a36Sopenharmony_ci if (ctx->mpeg4_profile_ctrl) 247162306a36Sopenharmony_ci ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, 247462306a36Sopenharmony_ci &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, 247562306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, 247662306a36Sopenharmony_ci V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); 247762306a36Sopenharmony_ci if (ctx->mpeg4_level_ctrl) 247862306a36Sopenharmony_ci ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 247962306a36Sopenharmony_ci} 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_cistatic const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { 248262306a36Sopenharmony_ci .id = V4L2_CID_CODA_MB_ERR_CNT, 248362306a36Sopenharmony_ci .name = "Macroblocks Error Count", 248462306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 248562306a36Sopenharmony_ci .min = 0, 248662306a36Sopenharmony_ci .max = 0x7fffffff, 248762306a36Sopenharmony_ci .step = 1, 248862306a36Sopenharmony_ci}; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_cistatic int coda_ctrls_setup(struct coda_ctx *ctx) 249162306a36Sopenharmony_ci{ 249262306a36Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->ctrls, 2); 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 249562306a36Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 249662306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 249762306a36Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 249862306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_ENCODER) { 249962306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 250062306a36Sopenharmony_ci V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 250162306a36Sopenharmony_ci 1, 1, 1, 1); 250262306a36Sopenharmony_ci if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG) 250362306a36Sopenharmony_ci coda_jpeg_encode_ctrls(ctx); 250462306a36Sopenharmony_ci else 250562306a36Sopenharmony_ci coda_encode_ctrls(ctx); 250662306a36Sopenharmony_ci } else { 250762306a36Sopenharmony_ci v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, 250862306a36Sopenharmony_ci V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 250962306a36Sopenharmony_ci 1, 1, 1, 1); 251062306a36Sopenharmony_ci if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) 251162306a36Sopenharmony_ci coda_decode_ctrls(ctx); 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, 251462306a36Sopenharmony_ci &coda_mb_err_cnt_ctrl_config, 251562306a36Sopenharmony_ci NULL); 251662306a36Sopenharmony_ci if (ctx->mb_err_cnt_ctrl) 251762306a36Sopenharmony_ci ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci if (ctx->ctrls.error) { 252162306a36Sopenharmony_ci v4l2_err(&ctx->dev->v4l2_dev, 252262306a36Sopenharmony_ci "control initialization error (%d)", 252362306a36Sopenharmony_ci ctx->ctrls.error); 252462306a36Sopenharmony_ci return -EINVAL; 252562306a36Sopenharmony_ci } 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci return v4l2_ctrl_handler_setup(&ctx->ctrls); 252862306a36Sopenharmony_ci} 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_cistatic int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) 253162306a36Sopenharmony_ci{ 253262306a36Sopenharmony_ci vq->drv_priv = ctx; 253362306a36Sopenharmony_ci vq->ops = &coda_qops; 253462306a36Sopenharmony_ci vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 253562306a36Sopenharmony_ci vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 253662306a36Sopenharmony_ci vq->lock = &ctx->dev->dev_mutex; 253762306a36Sopenharmony_ci /* One way to indicate end-of-stream for coda is to set the 253862306a36Sopenharmony_ci * bytesused == 0. However by default videobuf2 handles bytesused 253962306a36Sopenharmony_ci * equal to 0 as a special case and changes its value to the size 254062306a36Sopenharmony_ci * of the buffer. Set the allow_zero_bytesused flag, so 254162306a36Sopenharmony_ci * that videobuf2 will keep the value of bytesused intact. 254262306a36Sopenharmony_ci */ 254362306a36Sopenharmony_ci vq->allow_zero_bytesused = 1; 254462306a36Sopenharmony_ci /* 254562306a36Sopenharmony_ci * We might be fine with no buffers on some of the queues, but that 254662306a36Sopenharmony_ci * would need to be reflected in job_ready(). Currently we expect all 254762306a36Sopenharmony_ci * queues to have at least one buffer queued. 254862306a36Sopenharmony_ci */ 254962306a36Sopenharmony_ci vq->min_buffers_needed = 1; 255062306a36Sopenharmony_ci vq->dev = ctx->dev->dev; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci return vb2_queue_init(vq); 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ciint coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq, 255662306a36Sopenharmony_ci struct vb2_queue *dst_vq) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci int ret; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 256162306a36Sopenharmony_ci src_vq->io_modes = VB2_DMABUF | VB2_MMAP; 256262306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci ret = coda_queue_init(priv, src_vq); 256562306a36Sopenharmony_ci if (ret) 256662306a36Sopenharmony_ci return ret; 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 256962306a36Sopenharmony_ci dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; 257062306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci return coda_queue_init(priv, dst_vq); 257362306a36Sopenharmony_ci} 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ciint coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, 257662306a36Sopenharmony_ci struct vb2_queue *dst_vq) 257762306a36Sopenharmony_ci{ 257862306a36Sopenharmony_ci int ret; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 258162306a36Sopenharmony_ci src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; 258262306a36Sopenharmony_ci src_vq->mem_ops = &vb2_vmalloc_memops; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci ret = coda_queue_init(priv, src_vq); 258562306a36Sopenharmony_ci if (ret) 258662306a36Sopenharmony_ci return ret; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 258962306a36Sopenharmony_ci dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; 259062306a36Sopenharmony_ci dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; 259162306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci return coda_queue_init(priv, dst_vq); 259462306a36Sopenharmony_ci} 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci/* 259762306a36Sopenharmony_ci * File operations 259862306a36Sopenharmony_ci */ 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_cistatic int coda_open(struct file *file) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 260362306a36Sopenharmony_ci struct coda_dev *dev = video_get_drvdata(vdev); 260462306a36Sopenharmony_ci struct coda_ctx *ctx; 260562306a36Sopenharmony_ci unsigned int max = ~0; 260662306a36Sopenharmony_ci char *name; 260762306a36Sopenharmony_ci int ret; 260862306a36Sopenharmony_ci int idx; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 261162306a36Sopenharmony_ci if (!ctx) 261262306a36Sopenharmony_ci return -ENOMEM; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci if (dev->devtype->product == CODA_DX6) 261562306a36Sopenharmony_ci max = CODADX6_MAX_INSTANCES - 1; 261662306a36Sopenharmony_ci idx = ida_alloc_max(&dev->ida, max, GFP_KERNEL); 261762306a36Sopenharmony_ci if (idx < 0) { 261862306a36Sopenharmony_ci ret = idx; 261962306a36Sopenharmony_ci goto err_coda_max; 262062306a36Sopenharmony_ci } 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "context%d", idx); 262362306a36Sopenharmony_ci if (!name) { 262462306a36Sopenharmony_ci ret = -ENOMEM; 262562306a36Sopenharmony_ci goto err_coda_name_init; 262662306a36Sopenharmony_ci } 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root); 262962306a36Sopenharmony_ci kfree(name); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci ctx->cvd = to_coda_video_device(vdev); 263262306a36Sopenharmony_ci ctx->inst_type = ctx->cvd->type; 263362306a36Sopenharmony_ci ctx->ops = ctx->cvd->ops; 263462306a36Sopenharmony_ci ctx->use_bit = !ctx->cvd->direct; 263562306a36Sopenharmony_ci init_completion(&ctx->completion); 263662306a36Sopenharmony_ci INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); 263762306a36Sopenharmony_ci if (ctx->ops->seq_init_work) 263862306a36Sopenharmony_ci INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); 263962306a36Sopenharmony_ci if (ctx->ops->seq_end_work) 264062306a36Sopenharmony_ci INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); 264162306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 264262306a36Sopenharmony_ci file->private_data = &ctx->fh; 264362306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 264462306a36Sopenharmony_ci ctx->dev = dev; 264562306a36Sopenharmony_ci ctx->idx = idx; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci coda_dbg(1, ctx, "open instance (%p)\n", ctx); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci switch (dev->devtype->product) { 265062306a36Sopenharmony_ci case CODA_960: 265162306a36Sopenharmony_ci /* 265262306a36Sopenharmony_ci * Enabling the BWB when decoding can hang the firmware with 265362306a36Sopenharmony_ci * certain streams. The issue was tracked as ENGR00293425 by 265462306a36Sopenharmony_ci * Freescale. As a workaround, disable BWB for all decoders. 265562306a36Sopenharmony_ci * The enable_bwb module parameter allows to override this. 265662306a36Sopenharmony_ci */ 265762306a36Sopenharmony_ci if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) 265862306a36Sopenharmony_ci ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; 265962306a36Sopenharmony_ci fallthrough; 266062306a36Sopenharmony_ci case CODA_HX4: 266162306a36Sopenharmony_ci case CODA_7541: 266262306a36Sopenharmony_ci ctx->reg_idx = 0; 266362306a36Sopenharmony_ci break; 266462306a36Sopenharmony_ci default: 266562306a36Sopenharmony_ci ctx->reg_idx = idx; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci if (ctx->dev->vdoa && !disable_vdoa) { 266862306a36Sopenharmony_ci ctx->vdoa = vdoa_context_create(dev->vdoa); 266962306a36Sopenharmony_ci if (!ctx->vdoa) 267062306a36Sopenharmony_ci v4l2_warn(&dev->v4l2_dev, 267162306a36Sopenharmony_ci "Failed to create vdoa context: not using vdoa"); 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci ctx->use_vdoa = false; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci /* Power up and upload firmware if necessary */ 267662306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev->dev); 267762306a36Sopenharmony_ci if (ret < 0) { 267862306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); 267962306a36Sopenharmony_ci goto err_pm_get; 268062306a36Sopenharmony_ci } 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci ret = clk_prepare_enable(dev->clk_per); 268362306a36Sopenharmony_ci if (ret) 268462306a36Sopenharmony_ci goto err_clk_enable; 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci ret = clk_prepare_enable(dev->clk_ahb); 268762306a36Sopenharmony_ci if (ret) 268862306a36Sopenharmony_ci goto err_clk_ahb; 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci set_default_params(ctx); 269162306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, 269262306a36Sopenharmony_ci ctx->ops->queue_init); 269362306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 269462306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", 269762306a36Sopenharmony_ci __func__, ret); 269862306a36Sopenharmony_ci goto err_ctx_init; 269962306a36Sopenharmony_ci } 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci ret = coda_ctrls_setup(ctx); 270262306a36Sopenharmony_ci if (ret) { 270362306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); 270462306a36Sopenharmony_ci goto err_ctrls_setup; 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrls; 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci mutex_init(&ctx->bitstream_mutex); 271062306a36Sopenharmony_ci mutex_init(&ctx->buffer_mutex); 271162306a36Sopenharmony_ci mutex_init(&ctx->wakeup_mutex); 271262306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->buffer_meta_list); 271362306a36Sopenharmony_ci spin_lock_init(&ctx->buffer_meta_lock); 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci return 0; 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_cierr_ctrls_setup: 271862306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 271962306a36Sopenharmony_cierr_ctx_init: 272062306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_ahb); 272162306a36Sopenharmony_cierr_clk_ahb: 272262306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_per); 272362306a36Sopenharmony_cierr_clk_enable: 272462306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 272562306a36Sopenharmony_cierr_pm_get: 272662306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 272762306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 272862306a36Sopenharmony_cierr_coda_name_init: 272962306a36Sopenharmony_ci ida_free(&dev->ida, ctx->idx); 273062306a36Sopenharmony_cierr_coda_max: 273162306a36Sopenharmony_ci kfree(ctx); 273262306a36Sopenharmony_ci return ret; 273362306a36Sopenharmony_ci} 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_cistatic int coda_release(struct file *file) 273662306a36Sopenharmony_ci{ 273762306a36Sopenharmony_ci struct coda_dev *dev = video_drvdata(file); 273862306a36Sopenharmony_ci struct coda_ctx *ctx = fh_to_ctx(file->private_data); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci coda_dbg(1, ctx, "release instance (%p)\n", ctx); 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) 274362306a36Sopenharmony_ci coda_bit_stream_end_flag(ctx); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* If this instance is running, call .job_abort and wait for it to end */ 274662306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (ctx->vdoa) 274962306a36Sopenharmony_ci vdoa_context_destroy(ctx->vdoa); 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci /* In case the instance was not running, we still need to call SEQ_END */ 275262306a36Sopenharmony_ci if (ctx->ops->seq_end_work) { 275362306a36Sopenharmony_ci queue_work(dev->workqueue, &ctx->seq_end_work); 275462306a36Sopenharmony_ci flush_work(&ctx->seq_end_work); 275562306a36Sopenharmony_ci } 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci if (ctx->dev->devtype->product == CODA_DX6) 275862306a36Sopenharmony_ci coda_free_aux_buf(dev, &ctx->workbuf); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrls); 276162306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_ahb); 276262306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_per); 276362306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 276462306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 276562306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 276662306a36Sopenharmony_ci ida_free(&dev->ida, ctx->idx); 276762306a36Sopenharmony_ci if (ctx->ops->release) 276862306a36Sopenharmony_ci ctx->ops->release(ctx); 276962306a36Sopenharmony_ci debugfs_remove_recursive(ctx->debugfs_entry); 277062306a36Sopenharmony_ci kfree(ctx); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci return 0; 277362306a36Sopenharmony_ci} 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_cistatic const struct v4l2_file_operations coda_fops = { 277662306a36Sopenharmony_ci .owner = THIS_MODULE, 277762306a36Sopenharmony_ci .open = coda_open, 277862306a36Sopenharmony_ci .release = coda_release, 277962306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 278062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 278162306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 278262306a36Sopenharmony_ci}; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_cistatic int coda_hw_init(struct coda_dev *dev) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci u32 data; 278762306a36Sopenharmony_ci u16 *p; 278862306a36Sopenharmony_ci int i, ret; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci ret = clk_prepare_enable(dev->clk_per); 279162306a36Sopenharmony_ci if (ret) 279262306a36Sopenharmony_ci goto err_clk_per; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci ret = clk_prepare_enable(dev->clk_ahb); 279562306a36Sopenharmony_ci if (ret) 279662306a36Sopenharmony_ci goto err_clk_ahb; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci reset_control_reset(dev->rstc); 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci /* 280162306a36Sopenharmony_ci * Copy the first CODA_ISRAM_SIZE in the internal SRAM. 280262306a36Sopenharmony_ci * The 16-bit chars in the code buffer are in memory access 280362306a36Sopenharmony_ci * order, re-sort them to CODA order for register download. 280462306a36Sopenharmony_ci * Data in this SRAM survives a reboot. 280562306a36Sopenharmony_ci */ 280662306a36Sopenharmony_ci p = (u16 *)dev->codebuf.vaddr; 280762306a36Sopenharmony_ci if (dev->devtype->product == CODA_DX6) { 280862306a36Sopenharmony_ci for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { 280962306a36Sopenharmony_ci data = CODA_DOWN_ADDRESS_SET(i) | 281062306a36Sopenharmony_ci CODA_DOWN_DATA_SET(p[i ^ 1]); 281162306a36Sopenharmony_ci coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci } else { 281462306a36Sopenharmony_ci for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { 281562306a36Sopenharmony_ci data = CODA_DOWN_ADDRESS_SET(i) | 281662306a36Sopenharmony_ci CODA_DOWN_DATA_SET(p[round_down(i, 4) + 281762306a36Sopenharmony_ci 3 - (i % 4)]); 281862306a36Sopenharmony_ci coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci } 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci /* Clear registers */ 282362306a36Sopenharmony_ci for (i = 0; i < 64; i++) 282462306a36Sopenharmony_ci coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci /* Tell the BIT where to find everything it needs */ 282762306a36Sopenharmony_ci if (dev->devtype->product == CODA_960 || 282862306a36Sopenharmony_ci dev->devtype->product == CODA_7541 || 282962306a36Sopenharmony_ci dev->devtype->product == CODA_HX4) { 283062306a36Sopenharmony_ci coda_write(dev, dev->tempbuf.paddr, 283162306a36Sopenharmony_ci CODA_REG_BIT_TEMP_BUF_ADDR); 283262306a36Sopenharmony_ci coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); 283362306a36Sopenharmony_ci } else { 283462306a36Sopenharmony_ci coda_write(dev, dev->workbuf.paddr, 283562306a36Sopenharmony_ci CODA_REG_BIT_WORK_BUF_ADDR); 283662306a36Sopenharmony_ci } 283762306a36Sopenharmony_ci coda_write(dev, dev->codebuf.paddr, 283862306a36Sopenharmony_ci CODA_REG_BIT_CODE_BUF_ADDR); 283962306a36Sopenharmony_ci coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci /* Set default values */ 284262306a36Sopenharmony_ci switch (dev->devtype->product) { 284362306a36Sopenharmony_ci case CODA_DX6: 284462306a36Sopenharmony_ci coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, 284562306a36Sopenharmony_ci CODA_REG_BIT_STREAM_CTRL); 284662306a36Sopenharmony_ci break; 284762306a36Sopenharmony_ci default: 284862306a36Sopenharmony_ci coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, 284962306a36Sopenharmony_ci CODA_REG_BIT_STREAM_CTRL); 285062306a36Sopenharmony_ci } 285162306a36Sopenharmony_ci if (dev->devtype->product == CODA_960) 285262306a36Sopenharmony_ci coda_write(dev, CODA9_FRAME_ENABLE_BWB, 285362306a36Sopenharmony_ci CODA_REG_BIT_FRAME_MEM_CTRL); 285462306a36Sopenharmony_ci else 285562306a36Sopenharmony_ci coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci if (dev->devtype->product != CODA_DX6) 285862306a36Sopenharmony_ci coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci coda_write(dev, CODA_INT_INTERRUPT_ENABLE, 286162306a36Sopenharmony_ci CODA_REG_BIT_INT_ENABLE); 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci /* Reset VPU and start processor */ 286462306a36Sopenharmony_ci data = coda_read(dev, CODA_REG_BIT_CODE_RESET); 286562306a36Sopenharmony_ci data |= CODA_REG_RESET_ENABLE; 286662306a36Sopenharmony_ci coda_write(dev, data, CODA_REG_BIT_CODE_RESET); 286762306a36Sopenharmony_ci udelay(10); 286862306a36Sopenharmony_ci data &= ~CODA_REG_RESET_ENABLE; 286962306a36Sopenharmony_ci coda_write(dev, data, CODA_REG_BIT_CODE_RESET); 287062306a36Sopenharmony_ci coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_ahb); 287362306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_per); 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci return 0; 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_cierr_clk_ahb: 287862306a36Sopenharmony_ci clk_disable_unprepare(dev->clk_per); 287962306a36Sopenharmony_cierr_clk_per: 288062306a36Sopenharmony_ci return ret; 288162306a36Sopenharmony_ci} 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_cistatic int coda_register_device(struct coda_dev *dev, int i) 288462306a36Sopenharmony_ci{ 288562306a36Sopenharmony_ci struct video_device *vfd = &dev->vfd[i]; 288662306a36Sopenharmony_ci const char *name; 288762306a36Sopenharmony_ci int ret; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci if (i >= dev->devtype->num_vdevs) 289062306a36Sopenharmony_ci return -EINVAL; 289162306a36Sopenharmony_ci name = dev->devtype->vdevs[i]->name; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); 289462306a36Sopenharmony_ci vfd->fops = &coda_fops; 289562306a36Sopenharmony_ci vfd->ioctl_ops = &coda_ioctl_ops; 289662306a36Sopenharmony_ci vfd->release = video_device_release_empty; 289762306a36Sopenharmony_ci vfd->lock = &dev->dev_mutex; 289862306a36Sopenharmony_ci vfd->v4l2_dev = &dev->v4l2_dev; 289962306a36Sopenharmony_ci vfd->vfl_dir = VFL_DIR_M2M; 290062306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; 290162306a36Sopenharmony_ci video_set_drvdata(vfd, dev); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci /* Not applicable, use the selection API instead */ 290462306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); 290562306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); 290662306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci if (dev->devtype->vdevs[i]->type == CODA_INST_ENCODER) { 290962306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); 291062306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); 291162306a36Sopenharmony_ci if (dev->devtype->vdevs[i]->dst_formats[0] == V4L2_PIX_FMT_JPEG) { 291262306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); 291362306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); 291462306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci } else { 291762306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); 291862306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); 291962306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMESIZES); 292062306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_ENUM_FRAMEINTERVALS); 292162306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_G_PARM); 292262306a36Sopenharmony_ci v4l2_disable_ioctl(vfd, VIDIOC_S_PARM); 292362306a36Sopenharmony_ci } 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 292662306a36Sopenharmony_ci if (!ret) 292762306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", 292862306a36Sopenharmony_ci name, video_device_node_name(vfd)); 292962306a36Sopenharmony_ci return ret; 293062306a36Sopenharmony_ci} 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_cistatic void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, 293362306a36Sopenharmony_ci size_t size) 293462306a36Sopenharmony_ci{ 293562306a36Sopenharmony_ci u32 *src = (u32 *)buf; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci /* Check if the firmware has a 16-byte Freescale header, skip it */ 293862306a36Sopenharmony_ci if (buf[0] == 'M' && buf[1] == 'X') 293962306a36Sopenharmony_ci src += 4; 294062306a36Sopenharmony_ci /* 294162306a36Sopenharmony_ci * Check whether the firmware is in native order or pre-reordered for 294262306a36Sopenharmony_ci * memory access. The first instruction opcode always is 0xe40e. 294362306a36Sopenharmony_ci */ 294462306a36Sopenharmony_ci if (__le16_to_cpup((__le16 *)src) == 0xe40e) { 294562306a36Sopenharmony_ci u32 *dst = dev->codebuf.vaddr; 294662306a36Sopenharmony_ci int i; 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_ci /* Firmware in native order, reorder while copying */ 294962306a36Sopenharmony_ci if (dev->devtype->product == CODA_DX6) { 295062306a36Sopenharmony_ci for (i = 0; i < (size - 16) / 4; i++) 295162306a36Sopenharmony_ci dst[i] = (src[i] << 16) | (src[i] >> 16); 295262306a36Sopenharmony_ci } else { 295362306a36Sopenharmony_ci for (i = 0; i < (size - 16) / 4; i += 2) { 295462306a36Sopenharmony_ci dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); 295562306a36Sopenharmony_ci dst[i + 1] = (src[i] << 16) | (src[i] >> 16); 295662306a36Sopenharmony_ci } 295762306a36Sopenharmony_ci } 295862306a36Sopenharmony_ci } else { 295962306a36Sopenharmony_ci /* Copy the already reordered firmware image */ 296062306a36Sopenharmony_ci memcpy(dev->codebuf.vaddr, src, size); 296162306a36Sopenharmony_ci } 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_cistatic void coda_fw_callback(const struct firmware *fw, void *context); 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_cistatic int coda_firmware_request(struct coda_dev *dev) 296762306a36Sopenharmony_ci{ 296862306a36Sopenharmony_ci char *fw; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci if (dev->firmware >= ARRAY_SIZE(dev->devtype->firmware)) 297162306a36Sopenharmony_ci return -EINVAL; 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci fw = dev->devtype->firmware[dev->firmware]; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, 297662306a36Sopenharmony_ci coda_product_name(dev->devtype->product)); 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, 297962306a36Sopenharmony_ci GFP_KERNEL, dev, coda_fw_callback); 298062306a36Sopenharmony_ci} 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_cistatic void coda_fw_callback(const struct firmware *fw, void *context) 298362306a36Sopenharmony_ci{ 298462306a36Sopenharmony_ci struct coda_dev *dev = context; 298562306a36Sopenharmony_ci int i, ret; 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci if (!fw) { 298862306a36Sopenharmony_ci dev->firmware++; 298962306a36Sopenharmony_ci ret = coda_firmware_request(dev); 299062306a36Sopenharmony_ci if (ret < 0) { 299162306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); 299262306a36Sopenharmony_ci goto put_pm; 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci return; 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci if (dev->firmware > 0) { 299762306a36Sopenharmony_ci /* 299862306a36Sopenharmony_ci * Since we can't suppress warnings for failed asynchronous 299962306a36Sopenharmony_ci * firmware requests, report that the fallback firmware was 300062306a36Sopenharmony_ci * found. 300162306a36Sopenharmony_ci */ 300262306a36Sopenharmony_ci dev_info(dev->dev, "Using fallback firmware %s\n", 300362306a36Sopenharmony_ci dev->devtype->firmware[dev->firmware]); 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci /* allocate auxiliary per-device code buffer for the BIT processor */ 300762306a36Sopenharmony_ci ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", 300862306a36Sopenharmony_ci dev->debugfs_root); 300962306a36Sopenharmony_ci if (ret < 0) 301062306a36Sopenharmony_ci goto put_pm; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci coda_copy_firmware(dev, fw->data, fw->size); 301362306a36Sopenharmony_ci release_firmware(fw); 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci ret = coda_hw_init(dev); 301662306a36Sopenharmony_ci if (ret < 0) { 301762306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); 301862306a36Sopenharmony_ci goto put_pm; 301962306a36Sopenharmony_ci } 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci ret = coda_check_firmware(dev); 302262306a36Sopenharmony_ci if (ret < 0) 302362306a36Sopenharmony_ci goto put_pm; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); 302662306a36Sopenharmony_ci if (IS_ERR(dev->m2m_dev)) { 302762306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 302862306a36Sopenharmony_ci goto put_pm; 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci for (i = 0; i < dev->devtype->num_vdevs; i++) { 303262306a36Sopenharmony_ci ret = coda_register_device(dev, i); 303362306a36Sopenharmony_ci if (ret) { 303462306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 303562306a36Sopenharmony_ci "Failed to register %s video device: %d\n", 303662306a36Sopenharmony_ci dev->devtype->vdevs[i]->name, ret); 303762306a36Sopenharmony_ci goto rel_vfd; 303862306a36Sopenharmony_ci } 303962306a36Sopenharmony_ci } 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 304262306a36Sopenharmony_ci return; 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_cirel_vfd: 304562306a36Sopenharmony_ci while (--i >= 0) 304662306a36Sopenharmony_ci video_unregister_device(&dev->vfd[i]); 304762306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 304862306a36Sopenharmony_ciput_pm: 304962306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 305062306a36Sopenharmony_ci} 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_cienum coda_platform { 305362306a36Sopenharmony_ci CODA_IMX27, 305462306a36Sopenharmony_ci CODA_IMX51, 305562306a36Sopenharmony_ci CODA_IMX53, 305662306a36Sopenharmony_ci CODA_IMX6Q, 305762306a36Sopenharmony_ci CODA_IMX6DL, 305862306a36Sopenharmony_ci}; 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_cistatic const struct coda_devtype coda_devdata[] = { 306162306a36Sopenharmony_ci [CODA_IMX27] = { 306262306a36Sopenharmony_ci .firmware = { 306362306a36Sopenharmony_ci "vpu_fw_imx27_TO2.bin", 306462306a36Sopenharmony_ci "vpu/vpu_fw_imx27_TO2.bin", 306562306a36Sopenharmony_ci "v4l-codadx6-imx27.bin" 306662306a36Sopenharmony_ci }, 306762306a36Sopenharmony_ci .product = CODA_DX6, 306862306a36Sopenharmony_ci .codecs = codadx6_codecs, 306962306a36Sopenharmony_ci .num_codecs = ARRAY_SIZE(codadx6_codecs), 307062306a36Sopenharmony_ci .vdevs = codadx6_video_devices, 307162306a36Sopenharmony_ci .num_vdevs = ARRAY_SIZE(codadx6_video_devices), 307262306a36Sopenharmony_ci .workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024, 307362306a36Sopenharmony_ci .iram_size = 0xb000, 307462306a36Sopenharmony_ci }, 307562306a36Sopenharmony_ci [CODA_IMX51] = { 307662306a36Sopenharmony_ci .firmware = { 307762306a36Sopenharmony_ci "vpu_fw_imx51.bin", 307862306a36Sopenharmony_ci "vpu/vpu_fw_imx51.bin", 307962306a36Sopenharmony_ci "v4l-codahx4-imx51.bin" 308062306a36Sopenharmony_ci }, 308162306a36Sopenharmony_ci .product = CODA_HX4, 308262306a36Sopenharmony_ci .codecs = codahx4_codecs, 308362306a36Sopenharmony_ci .num_codecs = ARRAY_SIZE(codahx4_codecs), 308462306a36Sopenharmony_ci .vdevs = codahx4_video_devices, 308562306a36Sopenharmony_ci .num_vdevs = ARRAY_SIZE(codahx4_video_devices), 308662306a36Sopenharmony_ci .workbuf_size = 128 * 1024, 308762306a36Sopenharmony_ci .tempbuf_size = 304 * 1024, 308862306a36Sopenharmony_ci .iram_size = 0x14000, 308962306a36Sopenharmony_ci }, 309062306a36Sopenharmony_ci [CODA_IMX53] = { 309162306a36Sopenharmony_ci .firmware = { 309262306a36Sopenharmony_ci "vpu_fw_imx53.bin", 309362306a36Sopenharmony_ci "vpu/vpu_fw_imx53.bin", 309462306a36Sopenharmony_ci "v4l-coda7541-imx53.bin" 309562306a36Sopenharmony_ci }, 309662306a36Sopenharmony_ci .product = CODA_7541, 309762306a36Sopenharmony_ci .codecs = coda7_codecs, 309862306a36Sopenharmony_ci .num_codecs = ARRAY_SIZE(coda7_codecs), 309962306a36Sopenharmony_ci .vdevs = coda7_video_devices, 310062306a36Sopenharmony_ci .num_vdevs = ARRAY_SIZE(coda7_video_devices), 310162306a36Sopenharmony_ci .workbuf_size = 128 * 1024, 310262306a36Sopenharmony_ci .tempbuf_size = 304 * 1024, 310362306a36Sopenharmony_ci .iram_size = 0x14000, 310462306a36Sopenharmony_ci }, 310562306a36Sopenharmony_ci [CODA_IMX6Q] = { 310662306a36Sopenharmony_ci .firmware = { 310762306a36Sopenharmony_ci "vpu_fw_imx6q.bin", 310862306a36Sopenharmony_ci "vpu/vpu_fw_imx6q.bin", 310962306a36Sopenharmony_ci "v4l-coda960-imx6q.bin" 311062306a36Sopenharmony_ci }, 311162306a36Sopenharmony_ci .product = CODA_960, 311262306a36Sopenharmony_ci .codecs = coda9_codecs, 311362306a36Sopenharmony_ci .num_codecs = ARRAY_SIZE(coda9_codecs), 311462306a36Sopenharmony_ci .vdevs = coda9_video_devices, 311562306a36Sopenharmony_ci .num_vdevs = ARRAY_SIZE(coda9_video_devices), 311662306a36Sopenharmony_ci .workbuf_size = 80 * 1024, 311762306a36Sopenharmony_ci .tempbuf_size = 204 * 1024, 311862306a36Sopenharmony_ci .iram_size = 0x21000, 311962306a36Sopenharmony_ci }, 312062306a36Sopenharmony_ci [CODA_IMX6DL] = { 312162306a36Sopenharmony_ci .firmware = { 312262306a36Sopenharmony_ci "vpu_fw_imx6d.bin", 312362306a36Sopenharmony_ci "vpu/vpu_fw_imx6d.bin", 312462306a36Sopenharmony_ci "v4l-coda960-imx6dl.bin" 312562306a36Sopenharmony_ci }, 312662306a36Sopenharmony_ci .product = CODA_960, 312762306a36Sopenharmony_ci .codecs = coda9_codecs, 312862306a36Sopenharmony_ci .num_codecs = ARRAY_SIZE(coda9_codecs), 312962306a36Sopenharmony_ci .vdevs = coda9_video_devices, 313062306a36Sopenharmony_ci .num_vdevs = ARRAY_SIZE(coda9_video_devices), 313162306a36Sopenharmony_ci .workbuf_size = 80 * 1024, 313262306a36Sopenharmony_ci .tempbuf_size = 204 * 1024, 313362306a36Sopenharmony_ci .iram_size = 0x1f000, /* leave 4k for suspend code */ 313462306a36Sopenharmony_ci }, 313562306a36Sopenharmony_ci}; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_cistatic const struct of_device_id coda_dt_ids[] = { 313862306a36Sopenharmony_ci { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, 313962306a36Sopenharmony_ci { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, 314062306a36Sopenharmony_ci { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, 314162306a36Sopenharmony_ci { .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] }, 314262306a36Sopenharmony_ci { .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] }, 314362306a36Sopenharmony_ci { /* sentinel */ } 314462306a36Sopenharmony_ci}; 314562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, coda_dt_ids); 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_cistatic int coda_probe(struct platform_device *pdev) 314862306a36Sopenharmony_ci{ 314962306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 315062306a36Sopenharmony_ci struct gen_pool *pool; 315162306a36Sopenharmony_ci struct coda_dev *dev; 315262306a36Sopenharmony_ci int ret, irq; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 315562306a36Sopenharmony_ci if (!dev) 315662306a36Sopenharmony_ci return -ENOMEM; 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci dev->devtype = of_device_get_match_data(&pdev->dev); 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci dev->dev = &pdev->dev; 316162306a36Sopenharmony_ci dev->clk_per = devm_clk_get(&pdev->dev, "per"); 316262306a36Sopenharmony_ci if (IS_ERR(dev->clk_per)) { 316362306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not get per clock\n"); 316462306a36Sopenharmony_ci return PTR_ERR(dev->clk_per); 316562306a36Sopenharmony_ci } 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 316862306a36Sopenharmony_ci if (IS_ERR(dev->clk_ahb)) { 316962306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not get ahb clock\n"); 317062306a36Sopenharmony_ci return PTR_ERR(dev->clk_ahb); 317162306a36Sopenharmony_ci } 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ci /* Get memory for physical registers */ 317462306a36Sopenharmony_ci dev->regs_base = devm_platform_ioremap_resource(pdev, 0); 317562306a36Sopenharmony_ci if (IS_ERR(dev->regs_base)) 317662306a36Sopenharmony_ci return PTR_ERR(dev->regs_base); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci /* IRQ */ 317962306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, "bit"); 318062306a36Sopenharmony_ci if (irq < 0) 318162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 318262306a36Sopenharmony_ci if (irq < 0) 318362306a36Sopenharmony_ci return irq; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, 318662306a36Sopenharmony_ci CODA_NAME "-video", dev); 318762306a36Sopenharmony_ci if (ret < 0) { 318862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request irq: %d\n", ret); 318962306a36Sopenharmony_ci return ret; 319062306a36Sopenharmony_ci } 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci /* JPEG IRQ */ 319362306a36Sopenharmony_ci if (dev->devtype->product == CODA_960) { 319462306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, "jpeg"); 319562306a36Sopenharmony_ci if (irq < 0) 319662306a36Sopenharmony_ci return irq; 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 319962306a36Sopenharmony_ci coda9_jpeg_irq_handler, 320062306a36Sopenharmony_ci IRQF_ONESHOT, CODA_NAME "-jpeg", 320162306a36Sopenharmony_ci dev); 320262306a36Sopenharmony_ci if (ret < 0) { 320362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request jpeg irq\n"); 320462306a36Sopenharmony_ci return ret; 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ci } 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci dev->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, 320962306a36Sopenharmony_ci NULL); 321062306a36Sopenharmony_ci if (IS_ERR(dev->rstc)) { 321162306a36Sopenharmony_ci ret = PTR_ERR(dev->rstc); 321262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed get reset control: %d\n", ret); 321362306a36Sopenharmony_ci return ret; 321462306a36Sopenharmony_ci } 321562306a36Sopenharmony_ci 321662306a36Sopenharmony_ci /* Get IRAM pool from device tree */ 321762306a36Sopenharmony_ci pool = of_gen_pool_get(np, "iram", 0); 321862306a36Sopenharmony_ci if (!pool) { 321962306a36Sopenharmony_ci dev_err(&pdev->dev, "iram pool not available\n"); 322062306a36Sopenharmony_ci return -ENOMEM; 322162306a36Sopenharmony_ci } 322262306a36Sopenharmony_ci dev->iram_pool = pool; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci /* Get vdoa_data if supported by the platform */ 322562306a36Sopenharmony_ci dev->vdoa = coda_get_vdoa_data(); 322662306a36Sopenharmony_ci if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER) 322762306a36Sopenharmony_ci return -EPROBE_DEFER; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 323062306a36Sopenharmony_ci if (ret) 323162306a36Sopenharmony_ci return ret; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci ratelimit_default_init(&dev->mb_err_rs); 323462306a36Sopenharmony_ci mutex_init(&dev->dev_mutex); 323562306a36Sopenharmony_ci mutex_init(&dev->coda_mutex); 323662306a36Sopenharmony_ci ida_init(&dev->ida); 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci dev->debugfs_root = debugfs_create_dir("coda", NULL); 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci /* allocate auxiliary per-device buffers for the BIT processor */ 324162306a36Sopenharmony_ci if (dev->devtype->product == CODA_DX6) { 324262306a36Sopenharmony_ci ret = coda_alloc_aux_buf(dev, &dev->workbuf, 324362306a36Sopenharmony_ci dev->devtype->workbuf_size, "workbuf", 324462306a36Sopenharmony_ci dev->debugfs_root); 324562306a36Sopenharmony_ci if (ret < 0) 324662306a36Sopenharmony_ci goto err_v4l2_register; 324762306a36Sopenharmony_ci } 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci if (dev->devtype->tempbuf_size) { 325062306a36Sopenharmony_ci ret = coda_alloc_aux_buf(dev, &dev->tempbuf, 325162306a36Sopenharmony_ci dev->devtype->tempbuf_size, "tempbuf", 325262306a36Sopenharmony_ci dev->debugfs_root); 325362306a36Sopenharmony_ci if (ret < 0) 325462306a36Sopenharmony_ci goto err_v4l2_register; 325562306a36Sopenharmony_ci } 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci dev->iram.size = dev->devtype->iram_size; 325862306a36Sopenharmony_ci dev->iram.vaddr = gen_pool_dma_alloc(dev->iram_pool, dev->iram.size, 325962306a36Sopenharmony_ci &dev->iram.paddr); 326062306a36Sopenharmony_ci if (!dev->iram.vaddr) { 326162306a36Sopenharmony_ci dev_warn(&pdev->dev, "unable to alloc iram\n"); 326262306a36Sopenharmony_ci } else { 326362306a36Sopenharmony_ci memset(dev->iram.vaddr, 0, dev->iram.size); 326462306a36Sopenharmony_ci dev->iram.blob.data = dev->iram.vaddr; 326562306a36Sopenharmony_ci dev->iram.blob.size = dev->iram.size; 326662306a36Sopenharmony_ci dev->iram.dentry = debugfs_create_blob("iram", 0444, 326762306a36Sopenharmony_ci dev->debugfs_root, 326862306a36Sopenharmony_ci &dev->iram.blob); 326962306a36Sopenharmony_ci } 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci dev->workqueue = alloc_ordered_workqueue("coda", WQ_MEM_RECLAIM); 327262306a36Sopenharmony_ci if (!dev->workqueue) { 327362306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to alloc workqueue\n"); 327462306a36Sopenharmony_ci ret = -ENOMEM; 327562306a36Sopenharmony_ci goto err_v4l2_register; 327662306a36Sopenharmony_ci } 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_ci /* 328162306a36Sopenharmony_ci * Start activated so we can directly call coda_hw_init in 328262306a36Sopenharmony_ci * coda_fw_callback regardless of whether CONFIG_PM is 328362306a36Sopenharmony_ci * enabled or whether the device is associated with a PM domain. 328462306a36Sopenharmony_ci */ 328562306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 328662306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 328762306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_ci ret = coda_firmware_request(dev); 329062306a36Sopenharmony_ci if (ret) 329162306a36Sopenharmony_ci goto err_alloc_workqueue; 329262306a36Sopenharmony_ci return 0; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_cierr_alloc_workqueue: 329562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 329662306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 329762306a36Sopenharmony_ci destroy_workqueue(dev->workqueue); 329862306a36Sopenharmony_cierr_v4l2_register: 329962306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 330062306a36Sopenharmony_ci return ret; 330162306a36Sopenharmony_ci} 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_cistatic void coda_remove(struct platform_device *pdev) 330462306a36Sopenharmony_ci{ 330562306a36Sopenharmony_ci struct coda_dev *dev = platform_get_drvdata(pdev); 330662306a36Sopenharmony_ci int i; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) { 330962306a36Sopenharmony_ci if (video_get_drvdata(&dev->vfd[i])) 331062306a36Sopenharmony_ci video_unregister_device(&dev->vfd[i]); 331162306a36Sopenharmony_ci } 331262306a36Sopenharmony_ci if (dev->m2m_dev) 331362306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 331462306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 331562306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 331662306a36Sopenharmony_ci destroy_workqueue(dev->workqueue); 331762306a36Sopenharmony_ci if (dev->iram.vaddr) 331862306a36Sopenharmony_ci gen_pool_free(dev->iram_pool, (unsigned long)dev->iram.vaddr, 331962306a36Sopenharmony_ci dev->iram.size); 332062306a36Sopenharmony_ci coda_free_aux_buf(dev, &dev->codebuf); 332162306a36Sopenharmony_ci coda_free_aux_buf(dev, &dev->tempbuf); 332262306a36Sopenharmony_ci coda_free_aux_buf(dev, &dev->workbuf); 332362306a36Sopenharmony_ci debugfs_remove_recursive(dev->debugfs_root); 332462306a36Sopenharmony_ci ida_destroy(&dev->ida); 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci#ifdef CONFIG_PM 332862306a36Sopenharmony_cistatic int coda_runtime_resume(struct device *dev) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci struct coda_dev *cdev = dev_get_drvdata(dev); 333162306a36Sopenharmony_ci int ret = 0; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci if (dev->pm_domain && cdev->codebuf.vaddr) { 333462306a36Sopenharmony_ci ret = coda_hw_init(cdev); 333562306a36Sopenharmony_ci if (ret) 333662306a36Sopenharmony_ci v4l2_err(&cdev->v4l2_dev, "HW initialization failed\n"); 333762306a36Sopenharmony_ci } 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci return ret; 334062306a36Sopenharmony_ci} 334162306a36Sopenharmony_ci#endif 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_cistatic const struct dev_pm_ops coda_pm_ops = { 334462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL) 334562306a36Sopenharmony_ci}; 334662306a36Sopenharmony_ci 334762306a36Sopenharmony_cistatic struct platform_driver coda_driver = { 334862306a36Sopenharmony_ci .probe = coda_probe, 334962306a36Sopenharmony_ci .remove_new = coda_remove, 335062306a36Sopenharmony_ci .driver = { 335162306a36Sopenharmony_ci .name = CODA_NAME, 335262306a36Sopenharmony_ci .of_match_table = coda_dt_ids, 335362306a36Sopenharmony_ci .pm = &coda_pm_ops, 335462306a36Sopenharmony_ci }, 335562306a36Sopenharmony_ci}; 335662306a36Sopenharmony_ci 335762306a36Sopenharmony_cimodule_platform_driver(coda_driver); 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 336062306a36Sopenharmony_ciMODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); 336162306a36Sopenharmony_ciMODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); 3362