162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author: Mikhail Ulyanov 462306a36Sopenharmony_ci * Copyright (C) 2014-2015 Cogent Embedded, Inc. <source@cogentembedded.com> 562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Renesas Electronics Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This is based on the drivers/media/platform/samsung/s5p-jpeg driver by 862306a36Sopenharmony_ci * Andrzej Pietrasiewicz and Jacek Anaszewski. 962306a36Sopenharmony_ci * Some portions of code inspired by VSP1 driver by Laurent Pinchart. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * TODO in order of priority: 1262306a36Sopenharmony_ci * 1) Rotation 1362306a36Sopenharmony_ci * 2) Cropping 1462306a36Sopenharmony_ci * 3) V4L2_CID_JPEG_ACTIVE_MARKER 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/unaligned.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/string.h> 2962306a36Sopenharmony_ci#include <linux/videodev2.h> 3062306a36Sopenharmony_ci#include <media/jpeg.h> 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-fh.h> 3562306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 3662306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3762306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3862306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define DRV_NAME "rcar_jpu" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Align JPEG header end to cache line to make sure we will not have any issues 4562306a36Sopenharmony_ci * with cache; additionally to requirement (33.3.27 R01UH0501EJ0100 Rev.1.00) 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci#define JPU_JPEG_HDR_SIZE (ALIGN(0x258, L1_CACHE_BYTES)) 4862306a36Sopenharmony_ci#define JPU_JPEG_MAX_BYTES_PER_PIXEL 2 /* 16 bit precision format */ 4962306a36Sopenharmony_ci#define JPU_JPEG_MIN_SIZE 25 /* SOI + SOF + EOI */ 5062306a36Sopenharmony_ci#define JPU_JPEG_QTBL_SIZE 0x40 5162306a36Sopenharmony_ci#define JPU_JPEG_HDCTBL_SIZE 0x1c 5262306a36Sopenharmony_ci#define JPU_JPEG_HACTBL_SIZE 0xb2 5362306a36Sopenharmony_ci#define JPU_JPEG_HEIGHT_OFFSET 0x91 5462306a36Sopenharmony_ci#define JPU_JPEG_WIDTH_OFFSET 0x93 5562306a36Sopenharmony_ci#define JPU_JPEG_SUBS_OFFSET 0x97 5662306a36Sopenharmony_ci#define JPU_JPEG_QTBL_LUM_OFFSET 0x07 5762306a36Sopenharmony_ci#define JPU_JPEG_QTBL_CHR_OFFSET 0x4c 5862306a36Sopenharmony_ci#define JPU_JPEG_HDCTBL_LUM_OFFSET 0xa4 5962306a36Sopenharmony_ci#define JPU_JPEG_HACTBL_LUM_OFFSET 0xc5 6062306a36Sopenharmony_ci#define JPU_JPEG_HDCTBL_CHR_OFFSET 0x17c 6162306a36Sopenharmony_ci#define JPU_JPEG_HACTBL_CHR_OFFSET 0x19d 6262306a36Sopenharmony_ci#define JPU_JPEG_PADDING_OFFSET 0x24f 6362306a36Sopenharmony_ci#define JPU_JPEG_LUM 0x00 6462306a36Sopenharmony_ci#define JPU_JPEG_CHR 0x01 6562306a36Sopenharmony_ci#define JPU_JPEG_DC 0x00 6662306a36Sopenharmony_ci#define JPU_JPEG_AC 0x10 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define JPU_JPEG_422 0x21 6962306a36Sopenharmony_ci#define JPU_JPEG_420 0x22 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define JPU_JPEG_DEFAULT_422_PIX_FMT V4L2_PIX_FMT_NV16M 7262306a36Sopenharmony_ci#define JPU_JPEG_DEFAULT_420_PIX_FMT V4L2_PIX_FMT_NV12M 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define JPU_RESET_TIMEOUT 100 /* ms */ 7562306a36Sopenharmony_ci#define JPU_JOB_TIMEOUT 300 /* ms */ 7662306a36Sopenharmony_ci#define JPU_MAX_QUALITY 4 7762306a36Sopenharmony_ci#define JPU_WIDTH_MIN 16 7862306a36Sopenharmony_ci#define JPU_HEIGHT_MIN 16 7962306a36Sopenharmony_ci#define JPU_WIDTH_MAX 4096 8062306a36Sopenharmony_ci#define JPU_HEIGHT_MAX 4096 8162306a36Sopenharmony_ci#define JPU_MEMALIGN 8 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Flags that indicate a format can be used for capture/output */ 8462306a36Sopenharmony_ci#define JPU_FMT_TYPE_OUTPUT 0 8562306a36Sopenharmony_ci#define JPU_FMT_TYPE_CAPTURE 1 8662306a36Sopenharmony_ci#define JPU_ENC_CAPTURE (1 << 0) 8762306a36Sopenharmony_ci#define JPU_ENC_OUTPUT (1 << 1) 8862306a36Sopenharmony_ci#define JPU_DEC_CAPTURE (1 << 2) 8962306a36Sopenharmony_ci#define JPU_DEC_OUTPUT (1 << 3) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * JPEG registers and bits 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* JPEG code mode register */ 9662306a36Sopenharmony_ci#define JCMOD 0x00 9762306a36Sopenharmony_ci#define JCMOD_PCTR (1 << 7) 9862306a36Sopenharmony_ci#define JCMOD_MSKIP_ENABLE (1 << 5) 9962306a36Sopenharmony_ci#define JCMOD_DSP_ENC (0 << 3) 10062306a36Sopenharmony_ci#define JCMOD_DSP_DEC (1 << 3) 10162306a36Sopenharmony_ci#define JCMOD_REDU (7 << 0) 10262306a36Sopenharmony_ci#define JCMOD_REDU_422 (1 << 0) 10362306a36Sopenharmony_ci#define JCMOD_REDU_420 (2 << 0) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* JPEG code command register */ 10662306a36Sopenharmony_ci#define JCCMD 0x04 10762306a36Sopenharmony_ci#define JCCMD_SRST (1 << 12) 10862306a36Sopenharmony_ci#define JCCMD_JEND (1 << 2) 10962306a36Sopenharmony_ci#define JCCMD_JSRT (1 << 0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* JPEG code quantization table number register */ 11262306a36Sopenharmony_ci#define JCQTN 0x0c 11362306a36Sopenharmony_ci#define JCQTN_SHIFT(t) (((t) - 1) << 1) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* JPEG code Huffman table number register */ 11662306a36Sopenharmony_ci#define JCHTN 0x10 11762306a36Sopenharmony_ci#define JCHTN_AC_SHIFT(t) (((t) << 1) - 1) 11862306a36Sopenharmony_ci#define JCHTN_DC_SHIFT(t) (((t) - 1) << 1) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define JCVSZU 0x1c /* JPEG code vertical size upper register */ 12162306a36Sopenharmony_ci#define JCVSZD 0x20 /* JPEG code vertical size lower register */ 12262306a36Sopenharmony_ci#define JCHSZU 0x24 /* JPEG code horizontal size upper register */ 12362306a36Sopenharmony_ci#define JCHSZD 0x28 /* JPEG code horizontal size lower register */ 12462306a36Sopenharmony_ci#define JCSZ_MASK 0xff /* JPEG code h/v size register contains only 1 byte*/ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define JCDTCU 0x2c /* JPEG code data count upper register */ 12762306a36Sopenharmony_ci#define JCDTCM 0x30 /* JPEG code data count middle register */ 12862306a36Sopenharmony_ci#define JCDTCD 0x34 /* JPEG code data count lower register */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* JPEG interrupt enable register */ 13162306a36Sopenharmony_ci#define JINTE 0x38 13262306a36Sopenharmony_ci#define JINTE_ERR (7 << 5) /* INT5 + INT6 + INT7 */ 13362306a36Sopenharmony_ci#define JINTE_TRANSF_COMPL (1 << 10) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* JPEG interrupt status register */ 13662306a36Sopenharmony_ci#define JINTS 0x3c 13762306a36Sopenharmony_ci#define JINTS_MASK 0x7c68 13862306a36Sopenharmony_ci#define JINTS_ERR (1 << 5) 13962306a36Sopenharmony_ci#define JINTS_PROCESS_COMPL (1 << 6) 14062306a36Sopenharmony_ci#define JINTS_TRANSF_COMPL (1 << 10) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define JCDERR 0x40 /* JPEG code decode error register */ 14362306a36Sopenharmony_ci#define JCDERR_MASK 0xf /* JPEG code decode error register mask*/ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* JPEG interface encoding */ 14662306a36Sopenharmony_ci#define JIFECNT 0x70 14762306a36Sopenharmony_ci#define JIFECNT_INFT_422 0 14862306a36Sopenharmony_ci#define JIFECNT_INFT_420 1 14962306a36Sopenharmony_ci#define JIFECNT_SWAP_WB (3 << 4) /* to JPU */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define JIFESYA1 0x74 /* encode source Y address register 1 */ 15262306a36Sopenharmony_ci#define JIFESCA1 0x78 /* encode source C address register 1 */ 15362306a36Sopenharmony_ci#define JIFESYA2 0x7c /* encode source Y address register 2 */ 15462306a36Sopenharmony_ci#define JIFESCA2 0x80 /* encode source C address register 2 */ 15562306a36Sopenharmony_ci#define JIFESMW 0x84 /* encode source memory width register */ 15662306a36Sopenharmony_ci#define JIFESVSZ 0x88 /* encode source vertical size register */ 15762306a36Sopenharmony_ci#define JIFESHSZ 0x8c /* encode source horizontal size register */ 15862306a36Sopenharmony_ci#define JIFEDA1 0x90 /* encode destination address register 1 */ 15962306a36Sopenharmony_ci#define JIFEDA2 0x94 /* encode destination address register 2 */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* JPEG decoding control register */ 16262306a36Sopenharmony_ci#define JIFDCNT 0xa0 16362306a36Sopenharmony_ci#define JIFDCNT_SWAP_WB (3 << 1) /* from JPU */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#define JIFDSA1 0xa4 /* decode source address register 1 */ 16662306a36Sopenharmony_ci#define JIFDDMW 0xb0 /* decode destination memory width register */ 16762306a36Sopenharmony_ci#define JIFDDVSZ 0xb4 /* decode destination vert. size register */ 16862306a36Sopenharmony_ci#define JIFDDHSZ 0xb8 /* decode destination horiz. size register */ 16962306a36Sopenharmony_ci#define JIFDDYA1 0xbc /* decode destination Y address register 1 */ 17062306a36Sopenharmony_ci#define JIFDDCA1 0xc0 /* decode destination C address register 1 */ 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define JCQTBL(n) (0x10000 + (n) * 0x40) /* quantization tables regs */ 17362306a36Sopenharmony_ci#define JCHTBD(n) (0x10100 + (n) * 0x100) /* Huffman table DC regs */ 17462306a36Sopenharmony_ci#define JCHTBA(n) (0x10120 + (n) * 0x100) /* Huffman table AC regs */ 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * struct jpu - JPEG IP abstraction 17862306a36Sopenharmony_ci * @mutex: the mutex protecting this structure 17962306a36Sopenharmony_ci * @lock: spinlock protecting the device contexts 18062306a36Sopenharmony_ci * @v4l2_dev: v4l2 device for mem2mem mode 18162306a36Sopenharmony_ci * @vfd_encoder: video device node for encoder mem2mem mode 18262306a36Sopenharmony_ci * @vfd_decoder: video device node for decoder mem2mem mode 18362306a36Sopenharmony_ci * @m2m_dev: v4l2 mem2mem device data 18462306a36Sopenharmony_ci * @curr: pointer to current context 18562306a36Sopenharmony_ci * @regs: JPEG IP registers mapping 18662306a36Sopenharmony_ci * @irq: JPEG IP irq 18762306a36Sopenharmony_ci * @clk: JPEG IP clock 18862306a36Sopenharmony_ci * @dev: JPEG IP struct device 18962306a36Sopenharmony_ci * @ref_count: reference counter 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistruct jpu { 19262306a36Sopenharmony_ci struct mutex mutex; 19362306a36Sopenharmony_ci spinlock_t lock; 19462306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 19562306a36Sopenharmony_ci struct video_device vfd_encoder; 19662306a36Sopenharmony_ci struct video_device vfd_decoder; 19762306a36Sopenharmony_ci struct v4l2_m2m_dev *m2m_dev; 19862306a36Sopenharmony_ci struct jpu_ctx *curr; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci void __iomem *regs; 20162306a36Sopenharmony_ci unsigned int irq; 20262306a36Sopenharmony_ci struct clk *clk; 20362306a36Sopenharmony_ci struct device *dev; 20462306a36Sopenharmony_ci int ref_count; 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * struct jpu_buffer - driver's specific video buffer 20962306a36Sopenharmony_ci * @buf: m2m buffer 21062306a36Sopenharmony_ci * @compr_quality: destination image quality in compression mode 21162306a36Sopenharmony_ci * @subsampling: source image subsampling in decompression mode 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistruct jpu_buffer { 21462306a36Sopenharmony_ci struct v4l2_m2m_buffer buf; 21562306a36Sopenharmony_ci unsigned short compr_quality; 21662306a36Sopenharmony_ci unsigned char subsampling; 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * struct jpu_fmt - driver's internal format data 22162306a36Sopenharmony_ci * @fourcc: the fourcc code, 0 if not applicable 22262306a36Sopenharmony_ci * @colorspace: the colorspace specifier 22362306a36Sopenharmony_ci * @bpp: number of bits per pixel per plane 22462306a36Sopenharmony_ci * @h_align: horizontal alignment order (align to 2^h_align) 22562306a36Sopenharmony_ci * @v_align: vertical alignment order (align to 2^v_align) 22662306a36Sopenharmony_ci * @subsampling: (horizontal:4 | vertical:4) subsampling factor 22762306a36Sopenharmony_ci * @num_planes: number of planes 22862306a36Sopenharmony_ci * @types: types of queue this format is applicable to 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistruct jpu_fmt { 23162306a36Sopenharmony_ci u32 fourcc; 23262306a36Sopenharmony_ci u32 colorspace; 23362306a36Sopenharmony_ci u8 bpp[2]; 23462306a36Sopenharmony_ci u8 h_align; 23562306a36Sopenharmony_ci u8 v_align; 23662306a36Sopenharmony_ci u8 subsampling; 23762306a36Sopenharmony_ci u8 num_planes; 23862306a36Sopenharmony_ci u16 types; 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/** 24262306a36Sopenharmony_ci * struct jpu_q_data - parameters of one queue 24362306a36Sopenharmony_ci * @fmtinfo: driver-specific format of this queue 24462306a36Sopenharmony_ci * @format: multiplanar format of this queue 24562306a36Sopenharmony_ci * @sequence: sequence number 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistruct jpu_q_data { 24862306a36Sopenharmony_ci struct jpu_fmt *fmtinfo; 24962306a36Sopenharmony_ci struct v4l2_pix_format_mplane format; 25062306a36Sopenharmony_ci unsigned int sequence; 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/** 25462306a36Sopenharmony_ci * struct jpu_ctx - the device context data 25562306a36Sopenharmony_ci * @jpu: JPEG IP device for this context 25662306a36Sopenharmony_ci * @encoder: compression (encode) operation or decompression (decode) 25762306a36Sopenharmony_ci * @compr_quality: destination image quality in compression (encode) mode 25862306a36Sopenharmony_ci * @out_q: source (output) queue information 25962306a36Sopenharmony_ci * @cap_q: destination (capture) queue information 26062306a36Sopenharmony_ci * @fh: file handler 26162306a36Sopenharmony_ci * @ctrl_handler: controls handler 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistruct jpu_ctx { 26462306a36Sopenharmony_ci struct jpu *jpu; 26562306a36Sopenharmony_ci bool encoder; 26662306a36Sopenharmony_ci unsigned short compr_quality; 26762306a36Sopenharmony_ci struct jpu_q_data out_q; 26862306a36Sopenharmony_ci struct jpu_q_data cap_q; 26962306a36Sopenharmony_ci struct v4l2_fh fh; 27062306a36Sopenharmony_ci struct v4l2_ctrl_handler ctrl_handler; 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /** 27462306a36Sopenharmony_ci * jpeg_buffer - description of memory containing input JPEG data 27562306a36Sopenharmony_ci * @end: end position in the buffer 27662306a36Sopenharmony_ci * @curr: current position in the buffer 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_cistruct jpeg_buffer { 27962306a36Sopenharmony_ci void *end; 28062306a36Sopenharmony_ci void *curr; 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic struct jpu_fmt jpu_formats[] = { 28462306a36Sopenharmony_ci { V4L2_PIX_FMT_JPEG, V4L2_COLORSPACE_JPEG, 28562306a36Sopenharmony_ci {0, 0}, 0, 0, 0, 1, JPU_ENC_CAPTURE | JPU_DEC_OUTPUT }, 28662306a36Sopenharmony_ci { V4L2_PIX_FMT_NV16M, V4L2_COLORSPACE_SRGB, 28762306a36Sopenharmony_ci {8, 8}, 2, 2, JPU_JPEG_422, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, 28862306a36Sopenharmony_ci { V4L2_PIX_FMT_NV12M, V4L2_COLORSPACE_SRGB, 28962306a36Sopenharmony_ci {8, 4}, 2, 2, JPU_JPEG_420, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, 29062306a36Sopenharmony_ci { V4L2_PIX_FMT_NV16, V4L2_COLORSPACE_SRGB, 29162306a36Sopenharmony_ci {16, 0}, 2, 2, JPU_JPEG_422, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, 29262306a36Sopenharmony_ci { V4L2_PIX_FMT_NV12, V4L2_COLORSPACE_SRGB, 29362306a36Sopenharmony_ci {12, 0}, 2, 2, JPU_JPEG_420, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic const u8 zigzag[] = { 29762306a36Sopenharmony_ci 0x03, 0x02, 0x0b, 0x13, 0x0a, 0x01, 0x00, 0x09, 29862306a36Sopenharmony_ci 0x12, 0x1b, 0x23, 0x1a, 0x11, 0x08, 0x07, 0x06, 29962306a36Sopenharmony_ci 0x0f, 0x10, 0x19, 0x22, 0x2b, 0x33, 0x2a, 0x21, 30062306a36Sopenharmony_ci 0x18, 0x17, 0x0e, 0x05, 0x04, 0x0d, 0x16, 0x1f, 30162306a36Sopenharmony_ci 0x20, 0x29, 0x32, 0x3b, 0x3a, 0x31, 0x28, 0x27, 30262306a36Sopenharmony_ci 0x1e, 0x15, 0x0e, 0x14, 0x10, 0x26, 0x2f, 0x30, 30362306a36Sopenharmony_ci 0x39, 0x38, 0x37, 0x2e, 0x25, 0x1c, 0x24, 0x2b, 30462306a36Sopenharmony_ci 0x36, 0x3f, 0x3e, 0x35, 0x2c, 0x34, 0x3d, 0x3c 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci#define QTBL_SIZE (ALIGN(JPU_JPEG_QTBL_SIZE, \ 30862306a36Sopenharmony_ci sizeof(unsigned int)) / sizeof(unsigned int)) 30962306a36Sopenharmony_ci#define HDCTBL_SIZE (ALIGN(JPU_JPEG_HDCTBL_SIZE, \ 31062306a36Sopenharmony_ci sizeof(unsigned int)) / sizeof(unsigned int)) 31162306a36Sopenharmony_ci#define HACTBL_SIZE (ALIGN(JPU_JPEG_HACTBL_SIZE, \ 31262306a36Sopenharmony_ci sizeof(unsigned int)) / sizeof(unsigned int)) 31362306a36Sopenharmony_ci/* 31462306a36Sopenharmony_ci * Start of image; Quantization tables 31562306a36Sopenharmony_ci * SOF0 (17 bytes payload) is Baseline DCT - Sample precision, height, width, 31662306a36Sopenharmony_ci * Number of image components, (Ci:8 - Hi:4 - Vi:4 - Tq:8) * 3 - Y,Cb,Cr; 31762306a36Sopenharmony_ci * Huffman tables; Padding with 0xff (33.3.27 R01UH0501EJ0100 Rev.1.00) 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci#define JPU_JPEG_HDR_BLOB { \ 32062306a36Sopenharmony_ci 0xff, JPEG_MARKER_SOI, 0xff, JPEG_MARKER_DQT, 0x00, \ 32162306a36Sopenharmony_ci JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_LUM, \ 32262306a36Sopenharmony_ci [JPU_JPEG_QTBL_LUM_OFFSET ... \ 32362306a36Sopenharmony_ci JPU_JPEG_QTBL_LUM_OFFSET + JPU_JPEG_QTBL_SIZE - 1] = 0x00, \ 32462306a36Sopenharmony_ci 0xff, JPEG_MARKER_DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_CHR, \ 32562306a36Sopenharmony_ci [JPU_JPEG_QTBL_CHR_OFFSET ... JPU_JPEG_QTBL_CHR_OFFSET + \ 32662306a36Sopenharmony_ci JPU_JPEG_QTBL_SIZE - 1] = 0x00, \ 32762306a36Sopenharmony_ci 0xff, JPEG_MARKER_SOF0, 0x00, 0x11, 0x08, \ 32862306a36Sopenharmony_ci [JPU_JPEG_HEIGHT_OFFSET ... JPU_JPEG_HEIGHT_OFFSET + 1] = 0x00, \ 32962306a36Sopenharmony_ci [JPU_JPEG_WIDTH_OFFSET ... JPU_JPEG_WIDTH_OFFSET + 1] = 0x00, \ 33062306a36Sopenharmony_ci 0x03, 0x01, [JPU_JPEG_SUBS_OFFSET] = 0x00, JPU_JPEG_LUM, \ 33162306a36Sopenharmony_ci 0x02, 0x11, JPU_JPEG_CHR, 0x03, 0x11, JPU_JPEG_CHR, \ 33262306a36Sopenharmony_ci 0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, \ 33362306a36Sopenharmony_ci JPU_JPEG_LUM | JPU_JPEG_DC, \ 33462306a36Sopenharmony_ci [JPU_JPEG_HDCTBL_LUM_OFFSET ... \ 33562306a36Sopenharmony_ci JPU_JPEG_HDCTBL_LUM_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ 33662306a36Sopenharmony_ci 0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, \ 33762306a36Sopenharmony_ci JPU_JPEG_LUM | JPU_JPEG_AC, \ 33862306a36Sopenharmony_ci [JPU_JPEG_HACTBL_LUM_OFFSET ... \ 33962306a36Sopenharmony_ci JPU_JPEG_HACTBL_LUM_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ 34062306a36Sopenharmony_ci 0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, \ 34162306a36Sopenharmony_ci JPU_JPEG_CHR | JPU_JPEG_DC, \ 34262306a36Sopenharmony_ci [JPU_JPEG_HDCTBL_CHR_OFFSET ... \ 34362306a36Sopenharmony_ci JPU_JPEG_HDCTBL_CHR_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ 34462306a36Sopenharmony_ci 0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, \ 34562306a36Sopenharmony_ci JPU_JPEG_CHR | JPU_JPEG_AC, \ 34662306a36Sopenharmony_ci [JPU_JPEG_HACTBL_CHR_OFFSET ... \ 34762306a36Sopenharmony_ci JPU_JPEG_HACTBL_CHR_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ 34862306a36Sopenharmony_ci [JPU_JPEG_PADDING_OFFSET ... JPU_JPEG_HDR_SIZE - 1] = 0xff \ 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic unsigned char jpeg_hdrs[JPU_MAX_QUALITY][JPU_JPEG_HDR_SIZE] = { 35262306a36Sopenharmony_ci [0 ... JPU_MAX_QUALITY - 1] = JPU_JPEG_HDR_BLOB 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const unsigned int qtbl_lum[JPU_MAX_QUALITY][QTBL_SIZE] = { 35662306a36Sopenharmony_ci { 35762306a36Sopenharmony_ci 0x14101927, 0x322e3e44, 0x10121726, 0x26354144, 35862306a36Sopenharmony_ci 0x19171f26, 0x35414444, 0x27262635, 0x41444444, 35962306a36Sopenharmony_ci 0x32263541, 0x44444444, 0x2e354144, 0x44444444, 36062306a36Sopenharmony_ci 0x3e414444, 0x44444444, 0x44444444, 0x44444444 36162306a36Sopenharmony_ci }, 36262306a36Sopenharmony_ci { 36362306a36Sopenharmony_ci 0x100b0b10, 0x171b1f1e, 0x0b0c0c0f, 0x1417171e, 36462306a36Sopenharmony_ci 0x0b0c0d10, 0x171a232f, 0x100f1017, 0x1a252f40, 36562306a36Sopenharmony_ci 0x1714171a, 0x27334040, 0x1b171a25, 0x33404040, 36662306a36Sopenharmony_ci 0x1f17232f, 0x40404040, 0x1e1e2f40, 0x40404040 36762306a36Sopenharmony_ci }, 36862306a36Sopenharmony_ci { 36962306a36Sopenharmony_ci 0x0c08080c, 0x11151817, 0x0809090b, 0x0f131217, 37062306a36Sopenharmony_ci 0x08090a0c, 0x13141b24, 0x0c0b0c15, 0x141c2435, 37162306a36Sopenharmony_ci 0x110f1314, 0x1e27333b, 0x1513141c, 0x27333b3b, 37262306a36Sopenharmony_ci 0x18121b24, 0x333b3b3b, 0x17172435, 0x3b3b3b3b 37362306a36Sopenharmony_ci }, 37462306a36Sopenharmony_ci { 37562306a36Sopenharmony_ci 0x08060608, 0x0c0e1011, 0x06060608, 0x0a0d0c0f, 37662306a36Sopenharmony_ci 0x06060708, 0x0d0e1218, 0x0808080e, 0x0d131823, 37762306a36Sopenharmony_ci 0x0c0a0d0d, 0x141a2227, 0x0e0d0e13, 0x1a222727, 37862306a36Sopenharmony_ci 0x100c1318, 0x22272727, 0x110f1823, 0x27272727 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic const unsigned int qtbl_chr[JPU_MAX_QUALITY][QTBL_SIZE] = { 38362306a36Sopenharmony_ci { 38462306a36Sopenharmony_ci 0x15192026, 0x36444444, 0x191c1826, 0x36444444, 38562306a36Sopenharmony_ci 0x2018202b, 0x42444444, 0x26262b35, 0x44444444, 38662306a36Sopenharmony_ci 0x36424444, 0x44444444, 0x44444444, 0x44444444, 38762306a36Sopenharmony_ci 0x44444444, 0x44444444, 0x44444444, 0x44444444 38862306a36Sopenharmony_ci }, 38962306a36Sopenharmony_ci { 39062306a36Sopenharmony_ci 0x110f1115, 0x141a2630, 0x0f131211, 0x141a232b, 39162306a36Sopenharmony_ci 0x11121416, 0x1a1e2e35, 0x1511161c, 0x1e273540, 39262306a36Sopenharmony_ci 0x14141a1e, 0x27304040, 0x1a1a1e27, 0x303f4040, 39362306a36Sopenharmony_ci 0x26232e35, 0x40404040, 0x302b3540, 0x40404040 39462306a36Sopenharmony_ci }, 39562306a36Sopenharmony_ci { 39662306a36Sopenharmony_ci 0x0d0b0d10, 0x14141d25, 0x0b0e0e0e, 0x10141a20, 39762306a36Sopenharmony_ci 0x0d0e0f11, 0x14172328, 0x100e1115, 0x171e2832, 39862306a36Sopenharmony_ci 0x14101417, 0x1e25323b, 0x1414171e, 0x25303b3b, 39962306a36Sopenharmony_ci 0x1d1a2328, 0x323b3b3b, 0x25202832, 0x3b3b3b3b 40062306a36Sopenharmony_ci }, 40162306a36Sopenharmony_ci { 40262306a36Sopenharmony_ci 0x0908090b, 0x0e111318, 0x080a090b, 0x0e0d1116, 40362306a36Sopenharmony_ci 0x09090d0e, 0x0d0f171a, 0x0b0b0e0e, 0x0f141a21, 40462306a36Sopenharmony_ci 0x0e0e0d0f, 0x14182127, 0x110d0f14, 0x18202727, 40562306a36Sopenharmony_ci 0x1311171a, 0x21272727, 0x18161a21, 0x27272727 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const unsigned int hdctbl_lum[HDCTBL_SIZE] = { 41062306a36Sopenharmony_ci 0x00010501, 0x01010101, 0x01000000, 0x00000000, 41162306a36Sopenharmony_ci 0x00010203, 0x04050607, 0x08090a0b 41262306a36Sopenharmony_ci}; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic const unsigned int hdctbl_chr[HDCTBL_SIZE] = { 41562306a36Sopenharmony_ci 0x00010501, 0x01010101, 0x01000000, 0x00000000, 41662306a36Sopenharmony_ci 0x00010203, 0x04050607, 0x08090a0b 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const unsigned int hactbl_lum[HACTBL_SIZE] = { 42062306a36Sopenharmony_ci 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, 42162306a36Sopenharmony_ci 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, 42262306a36Sopenharmony_ci 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, 42362306a36Sopenharmony_ci 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, 42462306a36Sopenharmony_ci 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, 42562306a36Sopenharmony_ci 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, 42662306a36Sopenharmony_ci 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, 42762306a36Sopenharmony_ci 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const unsigned int hactbl_chr[HACTBL_SIZE] = { 43162306a36Sopenharmony_ci 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, 43262306a36Sopenharmony_ci 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, 43362306a36Sopenharmony_ci 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, 43462306a36Sopenharmony_ci 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, 43562306a36Sopenharmony_ci 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, 43662306a36Sopenharmony_ci 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, 43762306a36Sopenharmony_ci 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, 43862306a36Sopenharmony_ci 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 43962306a36Sopenharmony_ci}; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic const char *error_to_text[16] = { 44262306a36Sopenharmony_ci "Normal", 44362306a36Sopenharmony_ci "SOI not detected", 44462306a36Sopenharmony_ci "SOF1 to SOFF detected", 44562306a36Sopenharmony_ci "Subsampling not detected", 44662306a36Sopenharmony_ci "SOF accuracy error", 44762306a36Sopenharmony_ci "DQT accuracy error", 44862306a36Sopenharmony_ci "Component error 1", 44962306a36Sopenharmony_ci "Component error 2", 45062306a36Sopenharmony_ci "SOF0, DQT, and DHT not detected when SOS detected", 45162306a36Sopenharmony_ci "SOS not detected", 45262306a36Sopenharmony_ci "EOI not detected", 45362306a36Sopenharmony_ci "Restart interval data number error detected", 45462306a36Sopenharmony_ci "Image size error", 45562306a36Sopenharmony_ci "Last MCU data number error", 45662306a36Sopenharmony_ci "Block data number error", 45762306a36Sopenharmony_ci "Unknown" 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic struct jpu_buffer *vb2_to_jpu_buffer(struct vb2_v4l2_buffer *vb) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct v4l2_m2m_buffer *b = 46362306a36Sopenharmony_ci container_of(vb, struct v4l2_m2m_buffer, vb); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return container_of(b, struct jpu_buffer, buf); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic u32 jpu_read(struct jpu *jpu, unsigned int reg) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci return ioread32(jpu->regs + reg); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void jpu_write(struct jpu *jpu, u32 val, unsigned int reg) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci iowrite32(val, jpu->regs + reg); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic struct jpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci return container_of(c->handler, struct jpu_ctx, ctrl_handler); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic struct jpu_ctx *fh_to_ctx(struct v4l2_fh *fh) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci return container_of(fh, struct jpu_ctx, fh); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void jpu_set_tbl(struct jpu *jpu, u32 reg, const unsigned int *tbl, 48962306a36Sopenharmony_ci unsigned int len) { 49062306a36Sopenharmony_ci unsigned int i; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for (i = 0; i < len; i++) 49362306a36Sopenharmony_ci jpu_write(jpu, tbl[i], reg + (i << 2)); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void jpu_set_qtbl(struct jpu *jpu, unsigned short quality) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci jpu_set_tbl(jpu, JCQTBL(0), qtbl_lum[quality], QTBL_SIZE); 49962306a36Sopenharmony_ci jpu_set_tbl(jpu, JCQTBL(1), qtbl_chr[quality], QTBL_SIZE); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void jpu_set_htbl(struct jpu *jpu) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci jpu_set_tbl(jpu, JCHTBD(0), hdctbl_lum, HDCTBL_SIZE); 50562306a36Sopenharmony_ci jpu_set_tbl(jpu, JCHTBA(0), hactbl_lum, HACTBL_SIZE); 50662306a36Sopenharmony_ci jpu_set_tbl(jpu, JCHTBD(1), hdctbl_chr, HDCTBL_SIZE); 50762306a36Sopenharmony_ci jpu_set_tbl(jpu, JCHTBA(1), hactbl_chr, HACTBL_SIZE); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int jpu_wait_reset(struct jpu *jpu) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci unsigned long timeout; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(JPU_RESET_TIMEOUT); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci while (jpu_read(jpu, JCCMD) & JCCMD_SRST) { 51762306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 51862306a36Sopenharmony_ci dev_err(jpu->dev, "timed out in reset\n"); 51962306a36Sopenharmony_ci return -ETIMEDOUT; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci schedule(); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int jpu_reset(struct jpu *jpu) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci jpu_write(jpu, JCCMD_SRST, JCCMD); 53062306a36Sopenharmony_ci return jpu_wait_reset(jpu); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* 53462306a36Sopenharmony_ci * ============================================================================ 53562306a36Sopenharmony_ci * video ioctl operations 53662306a36Sopenharmony_ci * ============================================================================ 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic void put_qtbl(u8 *p, const u8 *qtbl) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci unsigned int i; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(zigzag); i++) 54362306a36Sopenharmony_ci p[i] = *(qtbl + zigzag[i]); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic void put_htbl(u8 *p, const u8 *htbl, unsigned int len) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci unsigned int i, j; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci for (i = 0; i < len; i += 4) 55162306a36Sopenharmony_ci for (j = 0; j < 4 && (i + j) < len; ++j) 55262306a36Sopenharmony_ci p[i + j] = htbl[i + 3 - j]; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic void jpu_generate_hdr(unsigned short quality, unsigned char *p) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci put_qtbl(p + JPU_JPEG_QTBL_LUM_OFFSET, (const u8 *)qtbl_lum[quality]); 55862306a36Sopenharmony_ci put_qtbl(p + JPU_JPEG_QTBL_CHR_OFFSET, (const u8 *)qtbl_chr[quality]); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci put_htbl(p + JPU_JPEG_HDCTBL_LUM_OFFSET, (const u8 *)hdctbl_lum, 56162306a36Sopenharmony_ci JPU_JPEG_HDCTBL_SIZE); 56262306a36Sopenharmony_ci put_htbl(p + JPU_JPEG_HACTBL_LUM_OFFSET, (const u8 *)hactbl_lum, 56362306a36Sopenharmony_ci JPU_JPEG_HACTBL_SIZE); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci put_htbl(p + JPU_JPEG_HDCTBL_CHR_OFFSET, (const u8 *)hdctbl_chr, 56662306a36Sopenharmony_ci JPU_JPEG_HDCTBL_SIZE); 56762306a36Sopenharmony_ci put_htbl(p + JPU_JPEG_HACTBL_CHR_OFFSET, (const u8 *)hactbl_chr, 56862306a36Sopenharmony_ci JPU_JPEG_HACTBL_SIZE); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int get_byte(struct jpeg_buffer *buf) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci if (buf->curr >= buf->end) 57462306a36Sopenharmony_ci return -1; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return *(u8 *)buf->curr++; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int get_word_be(struct jpeg_buffer *buf, unsigned int *word) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci if (buf->end - buf->curr < 2) 58262306a36Sopenharmony_ci return -1; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci *word = get_unaligned_be16(buf->curr); 58562306a36Sopenharmony_ci buf->curr += 2; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void skip(struct jpeg_buffer *buf, unsigned long len) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci buf->curr += min((unsigned long)(buf->end - buf->curr), len); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width, 59662306a36Sopenharmony_ci unsigned int *height) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct jpeg_buffer jpeg_buffer; 59962306a36Sopenharmony_ci unsigned int word; 60062306a36Sopenharmony_ci bool soi = false; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci jpeg_buffer.end = buffer + size; 60362306a36Sopenharmony_ci jpeg_buffer.curr = buffer; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* 60662306a36Sopenharmony_ci * basic size check and EOI - we don't want to let JPU cross 60762306a36Sopenharmony_ci * buffer bounds in any case. Hope it's stopping by EOI. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci if (size < JPU_JPEG_MIN_SIZE || 61062306a36Sopenharmony_ci *(u8 *)(buffer + size - 1) != JPEG_MARKER_EOI) 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci for (;;) { 61462306a36Sopenharmony_ci int c; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* skip preceding filler bytes */ 61762306a36Sopenharmony_ci do 61862306a36Sopenharmony_ci c = get_byte(&jpeg_buffer); 61962306a36Sopenharmony_ci while (c == 0xff || c == 0); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!soi && c == JPEG_MARKER_SOI) { 62262306a36Sopenharmony_ci soi = true; 62362306a36Sopenharmony_ci continue; 62462306a36Sopenharmony_ci } else if (soi != (c != JPEG_MARKER_SOI)) 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci switch (c) { 62862306a36Sopenharmony_ci case JPEG_MARKER_SOF0: /* SOF0: baseline JPEG */ 62962306a36Sopenharmony_ci skip(&jpeg_buffer, 3); /* segment length and bpp */ 63062306a36Sopenharmony_ci if (get_word_be(&jpeg_buffer, height) || 63162306a36Sopenharmony_ci get_word_be(&jpeg_buffer, width) || 63262306a36Sopenharmony_ci get_byte(&jpeg_buffer) != 3) /* YCbCr only */ 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci skip(&jpeg_buffer, 1); 63662306a36Sopenharmony_ci return get_byte(&jpeg_buffer); 63762306a36Sopenharmony_ci case JPEG_MARKER_DHT: 63862306a36Sopenharmony_ci case JPEG_MARKER_DQT: 63962306a36Sopenharmony_ci case JPEG_MARKER_COM: 64062306a36Sopenharmony_ci case JPEG_MARKER_DRI: 64162306a36Sopenharmony_ci case JPEG_MARKER_APP0 ... JPEG_MARKER_APP0 + 0x0f: 64262306a36Sopenharmony_ci if (get_word_be(&jpeg_buffer, &word)) 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci skip(&jpeg_buffer, (long)word - 2); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case 0: 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci default: 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int jpu_querycap(struct file *file, void *priv, 65762306a36Sopenharmony_ci struct v4l2_capability *cap) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (ctx->encoder) 66262306a36Sopenharmony_ci strscpy(cap->card, DRV_NAME " encoder", sizeof(cap->card)); 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci strscpy(cap->card, DRV_NAME " decoder", sizeof(cap->card)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); 66762306a36Sopenharmony_ci memset(cap->reserved, 0, sizeof(cap->reserved)); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic struct jpu_fmt *jpu_find_format(bool encoder, u32 pixelformat, 67362306a36Sopenharmony_ci unsigned int fmt_type) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci unsigned int i, fmt_flag; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (encoder) 67862306a36Sopenharmony_ci fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_ENC_OUTPUT : 67962306a36Sopenharmony_ci JPU_ENC_CAPTURE; 68062306a36Sopenharmony_ci else 68162306a36Sopenharmony_ci fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_DEC_OUTPUT : 68262306a36Sopenharmony_ci JPU_DEC_CAPTURE; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(jpu_formats); i++) { 68562306a36Sopenharmony_ci struct jpu_fmt *fmt = &jpu_formats[i]; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (fmt->fourcc == pixelformat && fmt->types & fmt_flag) 68862306a36Sopenharmony_ci return fmt; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return NULL; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int jpu_enum_fmt(struct v4l2_fmtdesc *f, u32 type) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci unsigned int i, num = 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(jpu_formats); ++i) { 69962306a36Sopenharmony_ci if (jpu_formats[i].types & type) { 70062306a36Sopenharmony_ci if (num == f->index) 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci ++num; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (i >= ARRAY_SIZE(jpu_formats)) 70762306a36Sopenharmony_ci return -EINVAL; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci f->pixelformat = jpu_formats[i].fourcc; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int jpu_enum_fmt_cap(struct file *file, void *priv, 71562306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_CAPTURE : 72062306a36Sopenharmony_ci JPU_DEC_CAPTURE); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int jpu_enum_fmt_out(struct file *file, void *priv, 72462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_OUTPUT : JPU_DEC_OUTPUT); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic struct jpu_q_data *jpu_get_q_data(struct jpu_ctx *ctx, 73262306a36Sopenharmony_ci enum v4l2_buf_type type) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) 73562306a36Sopenharmony_ci return &ctx->out_q; 73662306a36Sopenharmony_ci else 73762306a36Sopenharmony_ci return &ctx->cap_q; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void jpu_bound_align_image(u32 *w, unsigned int w_min, 74162306a36Sopenharmony_ci unsigned int w_max, unsigned int w_align, 74262306a36Sopenharmony_ci u32 *h, unsigned int h_min, 74362306a36Sopenharmony_ci unsigned int h_max, unsigned int h_align) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci unsigned int width, height, w_step, h_step; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci width = *w; 74862306a36Sopenharmony_ci height = *h; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci w_step = 1U << w_align; 75162306a36Sopenharmony_ci h_step = 1U << h_align; 75262306a36Sopenharmony_ci v4l_bound_align_image(w, w_min, w_max, w_align, h, h_min, h_max, 75362306a36Sopenharmony_ci h_align, 3); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (*w < width && *w + w_step < w_max) 75662306a36Sopenharmony_ci *w += w_step; 75762306a36Sopenharmony_ci if (*h < height && *h + h_step < h_max) 75862306a36Sopenharmony_ci *h += h_step; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, 76262306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix, 76362306a36Sopenharmony_ci enum v4l2_buf_type type) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct jpu_fmt *fmt; 76662306a36Sopenharmony_ci unsigned int f_type, w, h; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci f_type = V4L2_TYPE_IS_OUTPUT(type) ? JPU_FMT_TYPE_OUTPUT : 76962306a36Sopenharmony_ci JPU_FMT_TYPE_CAPTURE; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci fmt = jpu_find_format(ctx->encoder, pix->pixelformat, f_type); 77262306a36Sopenharmony_ci if (!fmt) { 77362306a36Sopenharmony_ci unsigned int pixelformat; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci dev_dbg(ctx->jpu->dev, "unknown format; set default format\n"); 77662306a36Sopenharmony_ci if (ctx->encoder) 77762306a36Sopenharmony_ci pixelformat = f_type == JPU_FMT_TYPE_OUTPUT ? 77862306a36Sopenharmony_ci V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; 77962306a36Sopenharmony_ci else 78062306a36Sopenharmony_ci pixelformat = f_type == JPU_FMT_TYPE_CAPTURE ? 78162306a36Sopenharmony_ci V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; 78262306a36Sopenharmony_ci fmt = jpu_find_format(ctx->encoder, pixelformat, f_type); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 78662306a36Sopenharmony_ci pix->colorspace = fmt->colorspace; 78762306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 78862306a36Sopenharmony_ci pix->num_planes = fmt->num_planes; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX, 79162306a36Sopenharmony_ci fmt->h_align, &pix->height, JPU_HEIGHT_MIN, 79262306a36Sopenharmony_ci JPU_HEIGHT_MAX, fmt->v_align); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci w = pix->width; 79562306a36Sopenharmony_ci h = pix->height; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { 79862306a36Sopenharmony_ci /* ignore userspaces's sizeimage for encoding */ 79962306a36Sopenharmony_ci if (pix->plane_fmt[0].sizeimage <= 0 || ctx->encoder) 80062306a36Sopenharmony_ci pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE + 80162306a36Sopenharmony_ci (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h); 80262306a36Sopenharmony_ci pix->plane_fmt[0].bytesperline = 0; 80362306a36Sopenharmony_ci } else { 80462306a36Sopenharmony_ci unsigned int i, bpl = 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci for (i = 0; i < pix->num_planes; ++i) 80762306a36Sopenharmony_ci bpl = max(bpl, pix->plane_fmt[i].bytesperline); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci bpl = clamp_t(unsigned int, bpl, w, JPU_WIDTH_MAX); 81062306a36Sopenharmony_ci bpl = round_up(bpl, JPU_MEMALIGN); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci for (i = 0; i < pix->num_planes; ++i) { 81362306a36Sopenharmony_ci pix->plane_fmt[i].bytesperline = bpl; 81462306a36Sopenharmony_ci pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (fmtinfo) 81962306a36Sopenharmony_ci *fmtinfo = fmt; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) 82962306a36Sopenharmony_ci return -EINVAL; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct vb2_queue *vq; 83762306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 83862306a36Sopenharmony_ci struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; 83962306a36Sopenharmony_ci struct jpu_fmt *fmtinfo; 84062306a36Sopenharmony_ci struct jpu_q_data *q_data; 84162306a36Sopenharmony_ci int ret; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(m2m_ctx, f->type); 84462306a36Sopenharmony_ci if (!vq) 84562306a36Sopenharmony_ci return -EINVAL; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 84862306a36Sopenharmony_ci v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__); 84962306a36Sopenharmony_ci return -EBUSY; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = __jpu_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci q_data = jpu_get_q_data(ctx, f->type); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci q_data->format = f->fmt.pix_mp; 85962306a36Sopenharmony_ci q_data->fmtinfo = fmtinfo; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct jpu_q_data *q_data; 86762306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) 87062306a36Sopenharmony_ci return -EINVAL; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci q_data = jpu_get_q_data(ctx, f->type); 87362306a36Sopenharmony_ci f->fmt.pix_mp = q_data->format; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/* 87962306a36Sopenharmony_ci * V4L2 controls 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic int jpu_s_ctrl(struct v4l2_ctrl *ctrl) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct jpu_ctx *ctx = ctrl_to_ctx(ctrl); 88462306a36Sopenharmony_ci unsigned long flags; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci spin_lock_irqsave(&ctx->jpu->lock, flags); 88762306a36Sopenharmony_ci if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) 88862306a36Sopenharmony_ci ctx->compr_quality = ctrl->val; 88962306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->jpu->lock, flags); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops jpu_ctrl_ops = { 89562306a36Sopenharmony_ci .s_ctrl = jpu_s_ctrl, 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(priv); 90162306a36Sopenharmony_ci struct jpu_q_data *src_q_data, *dst_q_data, *orig, adj, *ref; 90262306a36Sopenharmony_ci enum v4l2_buf_type adj_type; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci src_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 90562306a36Sopenharmony_ci dst_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (ctx->encoder) { 90862306a36Sopenharmony_ci adj = *src_q_data; 90962306a36Sopenharmony_ci orig = src_q_data; 91062306a36Sopenharmony_ci ref = dst_q_data; 91162306a36Sopenharmony_ci adj_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 91262306a36Sopenharmony_ci } else { 91362306a36Sopenharmony_ci adj = *dst_q_data; 91462306a36Sopenharmony_ci orig = dst_q_data; 91562306a36Sopenharmony_ci ref = src_q_data; 91662306a36Sopenharmony_ci adj_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci adj.format.width = ref->format.width; 92062306a36Sopenharmony_ci adj.format.height = ref->format.height; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci __jpu_try_fmt(ctx, NULL, &adj.format, adj_type); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (adj.format.width != orig->format.width || 92562306a36Sopenharmony_ci adj.format.height != orig->format.height) { 92662306a36Sopenharmony_ci dev_err(ctx->jpu->dev, "src and dst formats do not match.\n"); 92762306a36Sopenharmony_ci /* maybe we can return -EPIPE here? */ 92862306a36Sopenharmony_ci return -EINVAL; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops jpu_ioctl_ops = { 93562306a36Sopenharmony_ci .vidioc_querycap = jpu_querycap, 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = jpu_enum_fmt_cap, 93862306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = jpu_enum_fmt_out, 93962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt, 94062306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt, 94162306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt, 94262306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = jpu_try_fmt, 94362306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = jpu_s_fmt, 94462306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = jpu_s_fmt, 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 94762306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 94862306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 94962306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 95062306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 95162306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci .vidioc_streamon = jpu_streamon, 95462306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 95762306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe 95862306a36Sopenharmony_ci}; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int jpu_controls_create(struct jpu_ctx *ctx) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct v4l2_ctrl *ctrl; 96362306a36Sopenharmony_ci int ret; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, &jpu_ctrl_ops, 96862306a36Sopenharmony_ci V4L2_CID_JPEG_COMPRESSION_QUALITY, 96962306a36Sopenharmony_ci 0, JPU_MAX_QUALITY - 1, 1, 0); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (ctx->ctrl_handler.error) { 97262306a36Sopenharmony_ci ret = ctx->ctrl_handler.error; 97362306a36Sopenharmony_ci goto error_free; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (!ctx->encoder) 97762306a36Sopenharmony_ci ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | 97862306a36Sopenharmony_ci V4L2_CTRL_FLAG_READ_ONLY; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); 98162306a36Sopenharmony_ci if (ret < 0) 98262306a36Sopenharmony_ci goto error_free; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cierror_free: 98762306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 98862306a36Sopenharmony_ci return ret; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* 99262306a36Sopenharmony_ci * ============================================================================ 99362306a36Sopenharmony_ci * Queue operations 99462306a36Sopenharmony_ci * ============================================================================ 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_cistatic int jpu_queue_setup(struct vb2_queue *vq, 99762306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 99862306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vq); 100162306a36Sopenharmony_ci struct jpu_q_data *q_data; 100262306a36Sopenharmony_ci unsigned int i; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci q_data = jpu_get_q_data(ctx, vq->type); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (*nplanes) { 100762306a36Sopenharmony_ci if (*nplanes != q_data->format.num_planes) 100862306a36Sopenharmony_ci return -EINVAL; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci for (i = 0; i < *nplanes; i++) { 101162306a36Sopenharmony_ci unsigned int q_size = q_data->format.plane_fmt[i].sizeimage; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (sizes[i] < q_size) 101462306a36Sopenharmony_ci return -EINVAL; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci *nplanes = q_data->format.num_planes; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci for (i = 0; i < *nplanes; i++) 102262306a36Sopenharmony_ci sizes[i] = q_data->format.plane_fmt[i].sizeimage; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int jpu_buf_prepare(struct vb2_buffer *vb) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 103062306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 103162306a36Sopenharmony_ci struct jpu_q_data *q_data; 103262306a36Sopenharmony_ci unsigned int i; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci q_data = jpu_get_q_data(ctx, vb->vb2_queue->type); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { 103762306a36Sopenharmony_ci if (vbuf->field == V4L2_FIELD_ANY) 103862306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 103962306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_NONE) { 104062306a36Sopenharmony_ci dev_err(ctx->jpu->dev, "%s field isn't supported\n", 104162306a36Sopenharmony_ci __func__); 104262306a36Sopenharmony_ci return -EINVAL; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci for (i = 0; i < q_data->format.num_planes; i++) { 104762306a36Sopenharmony_ci unsigned long size = q_data->format.plane_fmt[i].sizeimage; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < size) { 105062306a36Sopenharmony_ci dev_err(ctx->jpu->dev, 105162306a36Sopenharmony_ci "%s: data will not fit into plane (%lu < %lu)\n", 105262306a36Sopenharmony_ci __func__, vb2_plane_size(vb, i), size); 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* decoder capture queue */ 105762306a36Sopenharmony_ci if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) 105862306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, size); 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic void jpu_buf_queue(struct vb2_buffer *vb) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 106762306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (!ctx->encoder && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { 107062306a36Sopenharmony_ci struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf); 107162306a36Sopenharmony_ci struct jpu_q_data *q_data, adjust; 107262306a36Sopenharmony_ci void *buffer = vb2_plane_vaddr(vb, 0); 107362306a36Sopenharmony_ci unsigned long buf_size = vb2_get_plane_payload(vb, 0); 107462306a36Sopenharmony_ci unsigned int width, height; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci u8 subsampling = jpu_parse_hdr(buffer, buf_size, &width, 107762306a36Sopenharmony_ci &height); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* check if JPEG data basic parsing was successful */ 108062306a36Sopenharmony_ci if (subsampling != JPU_JPEG_422 && subsampling != JPU_JPEG_420) 108162306a36Sopenharmony_ci goto format_error; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci q_data = &ctx->out_q; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci adjust = *q_data; 108662306a36Sopenharmony_ci adjust.format.width = width; 108762306a36Sopenharmony_ci adjust.format.height = height; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci __jpu_try_fmt(ctx, &adjust.fmtinfo, &adjust.format, 109062306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (adjust.format.width != q_data->format.width || 109362306a36Sopenharmony_ci adjust.format.height != q_data->format.height) 109462306a36Sopenharmony_ci goto format_error; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * keep subsampling in buffer to check it 109862306a36Sopenharmony_ci * for compatibility in device_run 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci jpu_buf->subsampling = subsampling; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (ctx->fh.m2m_ctx) 110462306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ciformat_error: 110962306a36Sopenharmony_ci dev_err(ctx->jpu->dev, "incompatible or corrupted JPEG data\n"); 111062306a36Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic void jpu_buf_finish(struct vb2_buffer *vb) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 111662306a36Sopenharmony_ci struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf); 111762306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 111862306a36Sopenharmony_ci struct jpu_q_data *q_data = &ctx->out_q; 111962306a36Sopenharmony_ci enum v4l2_buf_type type = vb->vb2_queue->type; 112062306a36Sopenharmony_ci u8 *buffer; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (vb->state == VB2_BUF_STATE_DONE) 112362306a36Sopenharmony_ci vbuf->sequence = jpu_get_q_data(ctx, type)->sequence++; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (!ctx->encoder || vb->state != VB2_BUF_STATE_DONE || 112662306a36Sopenharmony_ci V4L2_TYPE_IS_OUTPUT(type)) 112762306a36Sopenharmony_ci return; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci buffer = vb2_plane_vaddr(vb, 0); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci memcpy(buffer, jpeg_hdrs[jpu_buf->compr_quality], JPU_JPEG_HDR_SIZE); 113262306a36Sopenharmony_ci *(__be16 *)(buffer + JPU_JPEG_HEIGHT_OFFSET) = 113362306a36Sopenharmony_ci cpu_to_be16(q_data->format.height); 113462306a36Sopenharmony_ci *(__be16 *)(buffer + JPU_JPEG_WIDTH_OFFSET) = 113562306a36Sopenharmony_ci cpu_to_be16(q_data->format.width); 113662306a36Sopenharmony_ci *(buffer + JPU_JPEG_SUBS_OFFSET) = q_data->fmtinfo->subsampling; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic int jpu_start_streaming(struct vb2_queue *vq, unsigned count) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vq); 114262306a36Sopenharmony_ci struct jpu_q_data *q_data = jpu_get_q_data(ctx, vq->type); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci q_data->sequence = 0; 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic void jpu_stop_streaming(struct vb2_queue *vq) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct jpu_ctx *ctx = vb2_get_drv_priv(vq); 115162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vb; 115262306a36Sopenharmony_ci unsigned long flags; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci for (;;) { 115562306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vq->type)) 115662306a36Sopenharmony_ci vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 115762306a36Sopenharmony_ci else 115862306a36Sopenharmony_ci vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 115962306a36Sopenharmony_ci if (vb == NULL) 116062306a36Sopenharmony_ci return; 116162306a36Sopenharmony_ci spin_lock_irqsave(&ctx->jpu->lock, flags); 116262306a36Sopenharmony_ci v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); 116362306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->jpu->lock, flags); 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic const struct vb2_ops jpu_qops = { 116862306a36Sopenharmony_ci .queue_setup = jpu_queue_setup, 116962306a36Sopenharmony_ci .buf_prepare = jpu_buf_prepare, 117062306a36Sopenharmony_ci .buf_queue = jpu_buf_queue, 117162306a36Sopenharmony_ci .buf_finish = jpu_buf_finish, 117262306a36Sopenharmony_ci .start_streaming = jpu_start_streaming, 117362306a36Sopenharmony_ci .stop_streaming = jpu_stop_streaming, 117462306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 117562306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 117662306a36Sopenharmony_ci}; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic int jpu_queue_init(void *priv, struct vb2_queue *src_vq, 117962306a36Sopenharmony_ci struct vb2_queue *dst_vq) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct jpu_ctx *ctx = priv; 118262306a36Sopenharmony_ci int ret; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci memset(src_vq, 0, sizeof(*src_vq)); 118562306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 118662306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 118762306a36Sopenharmony_ci src_vq->drv_priv = ctx; 118862306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct jpu_buffer); 118962306a36Sopenharmony_ci src_vq->ops = &jpu_qops; 119062306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 119162306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 119262306a36Sopenharmony_ci src_vq->lock = &ctx->jpu->mutex; 119362306a36Sopenharmony_ci src_vq->dev = ctx->jpu->v4l2_dev.dev; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 119662306a36Sopenharmony_ci if (ret) 119762306a36Sopenharmony_ci return ret; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci memset(dst_vq, 0, sizeof(*dst_vq)); 120062306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 120162306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 120262306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 120362306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct jpu_buffer); 120462306a36Sopenharmony_ci dst_vq->ops = &jpu_qops; 120562306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 120662306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 120762306a36Sopenharmony_ci dst_vq->lock = &ctx->jpu->mutex; 120862306a36Sopenharmony_ci dst_vq->dev = ctx->jpu->v4l2_dev.dev; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci/* 121462306a36Sopenharmony_ci * ============================================================================ 121562306a36Sopenharmony_ci * Device file operations 121662306a36Sopenharmony_ci * ============================================================================ 121762306a36Sopenharmony_ci */ 121862306a36Sopenharmony_cistatic int jpu_open(struct file *file) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct jpu *jpu = video_drvdata(file); 122162306a36Sopenharmony_ci struct video_device *vfd = video_devdata(file); 122262306a36Sopenharmony_ci struct jpu_ctx *ctx; 122362306a36Sopenharmony_ci int ret; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 122662306a36Sopenharmony_ci if (!ctx) 122762306a36Sopenharmony_ci return -ENOMEM; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, vfd); 123062306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 123162306a36Sopenharmony_ci file->private_data = &ctx->fh; 123262306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci ctx->jpu = jpu; 123562306a36Sopenharmony_ci ctx->encoder = vfd == &jpu->vfd_encoder; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci __jpu_try_fmt(ctx, &ctx->out_q.fmtinfo, &ctx->out_q.format, 123862306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 123962306a36Sopenharmony_ci __jpu_try_fmt(ctx, &ctx->cap_q.fmtinfo, &ctx->cap_q.format, 124062306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpu->m2m_dev, ctx, jpu_queue_init); 124362306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 124462306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 124562306a36Sopenharmony_ci goto v4l_prepare_rollback; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci ret = jpu_controls_create(ctx); 124962306a36Sopenharmony_ci if (ret < 0) 125062306a36Sopenharmony_ci goto v4l_prepare_rollback; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (mutex_lock_interruptible(&jpu->mutex)) { 125362306a36Sopenharmony_ci ret = -ERESTARTSYS; 125462306a36Sopenharmony_ci goto v4l_prepare_rollback; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci if (jpu->ref_count == 0) { 125862306a36Sopenharmony_ci ret = clk_prepare_enable(jpu->clk); 125962306a36Sopenharmony_ci if (ret < 0) 126062306a36Sopenharmony_ci goto device_prepare_rollback; 126162306a36Sopenharmony_ci /* ...issue software reset */ 126262306a36Sopenharmony_ci ret = jpu_reset(jpu); 126362306a36Sopenharmony_ci if (ret) 126462306a36Sopenharmony_ci goto jpu_reset_rollback; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci jpu->ref_count++; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci mutex_unlock(&jpu->mutex); 127062306a36Sopenharmony_ci return 0; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cijpu_reset_rollback: 127362306a36Sopenharmony_ci clk_disable_unprepare(jpu->clk); 127462306a36Sopenharmony_cidevice_prepare_rollback: 127562306a36Sopenharmony_ci mutex_unlock(&jpu->mutex); 127662306a36Sopenharmony_civ4l_prepare_rollback: 127762306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 127862306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 127962306a36Sopenharmony_ci kfree(ctx); 128062306a36Sopenharmony_ci return ret; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int jpu_release(struct file *file) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct jpu *jpu = video_drvdata(file); 128662306a36Sopenharmony_ci struct jpu_ctx *ctx = fh_to_ctx(file->private_data); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 128962306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 129062306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 129162306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 129262306a36Sopenharmony_ci kfree(ctx); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci mutex_lock(&jpu->mutex); 129562306a36Sopenharmony_ci if (--jpu->ref_count == 0) 129662306a36Sopenharmony_ci clk_disable_unprepare(jpu->clk); 129762306a36Sopenharmony_ci mutex_unlock(&jpu->mutex); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic const struct v4l2_file_operations jpu_fops = { 130362306a36Sopenharmony_ci .owner = THIS_MODULE, 130462306a36Sopenharmony_ci .open = jpu_open, 130562306a36Sopenharmony_ci .release = jpu_release, 130662306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 130762306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 130862306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 130962306a36Sopenharmony_ci}; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci/* 131262306a36Sopenharmony_ci * ============================================================================ 131362306a36Sopenharmony_ci * mem2mem callbacks 131462306a36Sopenharmony_ci * ============================================================================ 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_cistatic void jpu_cleanup(struct jpu_ctx *ctx, bool reset) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci /* remove current buffers and finish job */ 131962306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 132062306a36Sopenharmony_ci unsigned long flags; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci spin_lock_irqsave(&ctx->jpu->lock, flags); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 132562306a36Sopenharmony_ci dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); 132862306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* ...and give it a chance on next run */ 133162306a36Sopenharmony_ci if (reset) 133262306a36Sopenharmony_ci jpu_write(ctx->jpu, JCCMD_SRST, JCCMD); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->jpu->lock, flags); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci v4l2_m2m_job_finish(ctx->jpu->m2m_dev, ctx->fh.m2m_ctx); 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic void jpu_device_run(void *priv) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct jpu_ctx *ctx = priv; 134262306a36Sopenharmony_ci struct jpu *jpu = ctx->jpu; 134362306a36Sopenharmony_ci struct jpu_buffer *jpu_buf; 134462306a36Sopenharmony_ci struct jpu_q_data *q_data; 134562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 134662306a36Sopenharmony_ci unsigned int w, h, bpl; 134762306a36Sopenharmony_ci unsigned char num_planes, subsampling; 134862306a36Sopenharmony_ci unsigned long flags; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* ...wait until module reset completes; we have mutex locked here */ 135162306a36Sopenharmony_ci if (jpu_wait_reset(jpu)) { 135262306a36Sopenharmony_ci jpu_cleanup(ctx, true); 135362306a36Sopenharmony_ci return; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci spin_lock_irqsave(&ctx->jpu->lock, flags); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci jpu->curr = ctx; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 136162306a36Sopenharmony_ci dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (ctx->encoder) { 136462306a36Sopenharmony_ci jpu_buf = vb2_to_jpu_buffer(dst_buf); 136562306a36Sopenharmony_ci q_data = &ctx->out_q; 136662306a36Sopenharmony_ci } else { 136762306a36Sopenharmony_ci jpu_buf = vb2_to_jpu_buffer(src_buf); 136862306a36Sopenharmony_ci q_data = &ctx->cap_q; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci w = q_data->format.width; 137262306a36Sopenharmony_ci h = q_data->format.height; 137362306a36Sopenharmony_ci bpl = q_data->format.plane_fmt[0].bytesperline; 137462306a36Sopenharmony_ci num_planes = q_data->fmtinfo->num_planes; 137562306a36Sopenharmony_ci subsampling = q_data->fmtinfo->subsampling; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (ctx->encoder) { 137862306a36Sopenharmony_ci unsigned long src_1_addr, src_2_addr, dst_addr; 137962306a36Sopenharmony_ci unsigned int redu, inft; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); 138262306a36Sopenharmony_ci src_1_addr = 138362306a36Sopenharmony_ci vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); 138462306a36Sopenharmony_ci if (num_planes > 1) 138562306a36Sopenharmony_ci src_2_addr = vb2_dma_contig_plane_dma_addr( 138662306a36Sopenharmony_ci &src_buf->vb2_buf, 1); 138762306a36Sopenharmony_ci else 138862306a36Sopenharmony_ci src_2_addr = src_1_addr + w * h; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci jpu_buf->compr_quality = ctx->compr_quality; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (subsampling == JPU_JPEG_420) { 139362306a36Sopenharmony_ci redu = JCMOD_REDU_420; 139462306a36Sopenharmony_ci inft = JIFECNT_INFT_420; 139562306a36Sopenharmony_ci } else { 139662306a36Sopenharmony_ci redu = JCMOD_REDU_422; 139762306a36Sopenharmony_ci inft = JIFECNT_INFT_422; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* only no marker mode works for encoding */ 140162306a36Sopenharmony_ci jpu_write(jpu, JCMOD_DSP_ENC | JCMOD_PCTR | redu | 140262306a36Sopenharmony_ci JCMOD_MSKIP_ENABLE, JCMOD); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci jpu_write(jpu, JIFECNT_SWAP_WB | inft, JIFECNT); 140562306a36Sopenharmony_ci jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); 140662306a36Sopenharmony_ci jpu_write(jpu, JINTE_TRANSF_COMPL, JINTE); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* Y and C components source addresses */ 140962306a36Sopenharmony_ci jpu_write(jpu, src_1_addr, JIFESYA1); 141062306a36Sopenharmony_ci jpu_write(jpu, src_2_addr, JIFESCA1); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* memory width */ 141362306a36Sopenharmony_ci jpu_write(jpu, bpl, JIFESMW); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci jpu_write(jpu, (w >> 8) & JCSZ_MASK, JCHSZU); 141662306a36Sopenharmony_ci jpu_write(jpu, w & JCSZ_MASK, JCHSZD); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci jpu_write(jpu, (h >> 8) & JCSZ_MASK, JCVSZU); 141962306a36Sopenharmony_ci jpu_write(jpu, h & JCSZ_MASK, JCVSZD); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci jpu_write(jpu, w, JIFESHSZ); 142262306a36Sopenharmony_ci jpu_write(jpu, h, JIFESVSZ); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci jpu_write(jpu, dst_addr + JPU_JPEG_HDR_SIZE, JIFEDA1); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci jpu_write(jpu, 0 << JCQTN_SHIFT(1) | 1 << JCQTN_SHIFT(2) | 142762306a36Sopenharmony_ci 1 << JCQTN_SHIFT(3), JCQTN); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci jpu_write(jpu, 0 << JCHTN_AC_SHIFT(1) | 0 << JCHTN_DC_SHIFT(1) | 143062306a36Sopenharmony_ci 1 << JCHTN_AC_SHIFT(2) | 1 << JCHTN_DC_SHIFT(2) | 143162306a36Sopenharmony_ci 1 << JCHTN_AC_SHIFT(3) | 1 << JCHTN_DC_SHIFT(3), 143262306a36Sopenharmony_ci JCHTN); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci jpu_set_qtbl(jpu, ctx->compr_quality); 143562306a36Sopenharmony_ci jpu_set_htbl(jpu); 143662306a36Sopenharmony_ci } else { 143762306a36Sopenharmony_ci unsigned long src_addr, dst_1_addr, dst_2_addr; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (jpu_buf->subsampling != subsampling) { 144062306a36Sopenharmony_ci dev_err(ctx->jpu->dev, 144162306a36Sopenharmony_ci "src and dst formats do not match.\n"); 144262306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->jpu->lock, flags); 144362306a36Sopenharmony_ci jpu_cleanup(ctx, false); 144462306a36Sopenharmony_ci return; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); 144862306a36Sopenharmony_ci dst_1_addr = 144962306a36Sopenharmony_ci vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); 145062306a36Sopenharmony_ci if (q_data->fmtinfo->num_planes > 1) 145162306a36Sopenharmony_ci dst_2_addr = vb2_dma_contig_plane_dma_addr( 145262306a36Sopenharmony_ci &dst_buf->vb2_buf, 1); 145362306a36Sopenharmony_ci else 145462306a36Sopenharmony_ci dst_2_addr = dst_1_addr + w * h; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* ...set up decoder operation */ 145762306a36Sopenharmony_ci jpu_write(jpu, JCMOD_DSP_DEC | JCMOD_PCTR, JCMOD); 145862306a36Sopenharmony_ci jpu_write(jpu, JIFECNT_SWAP_WB, JIFECNT); 145962306a36Sopenharmony_ci jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci /* ...enable interrupts on transfer completion and d-g error */ 146262306a36Sopenharmony_ci jpu_write(jpu, JINTE_TRANSF_COMPL | JINTE_ERR, JINTE); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci /* ...set source/destination addresses of encoded data */ 146562306a36Sopenharmony_ci jpu_write(jpu, src_addr, JIFDSA1); 146662306a36Sopenharmony_ci jpu_write(jpu, dst_1_addr, JIFDDYA1); 146762306a36Sopenharmony_ci jpu_write(jpu, dst_2_addr, JIFDDCA1); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci jpu_write(jpu, bpl, JIFDDMW); 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* ...start encoder/decoder operation */ 147362306a36Sopenharmony_ci jpu_write(jpu, JCCMD_JSRT, JCCMD); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->jpu->lock, flags); 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic const struct v4l2_m2m_ops jpu_m2m_ops = { 147962306a36Sopenharmony_ci .device_run = jpu_device_run, 148062306a36Sopenharmony_ci}; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci/* 148362306a36Sopenharmony_ci * ============================================================================ 148462306a36Sopenharmony_ci * IRQ handler 148562306a36Sopenharmony_ci * ============================================================================ 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_cistatic irqreturn_t jpu_irq_handler(int irq, void *dev_id) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci struct jpu *jpu = dev_id; 149062306a36Sopenharmony_ci struct jpu_ctx *curr_ctx; 149162306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 149262306a36Sopenharmony_ci unsigned int int_status; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci int_status = jpu_read(jpu, JINTS); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* ...spurious interrupt */ 149762306a36Sopenharmony_ci if (!((JINTS_TRANSF_COMPL | JINTS_PROCESS_COMPL | JINTS_ERR) & 149862306a36Sopenharmony_ci int_status)) 149962306a36Sopenharmony_ci return IRQ_NONE; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* ...clear interrupts */ 150262306a36Sopenharmony_ci jpu_write(jpu, ~(int_status & JINTS_MASK), JINTS); 150362306a36Sopenharmony_ci if (int_status & (JINTS_ERR | JINTS_PROCESS_COMPL)) 150462306a36Sopenharmony_ci jpu_write(jpu, JCCMD_JEND, JCCMD); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci spin_lock(&jpu->lock); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if ((int_status & JINTS_PROCESS_COMPL) && 150962306a36Sopenharmony_ci !(int_status & JINTS_TRANSF_COMPL)) 151062306a36Sopenharmony_ci goto handled; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci curr_ctx = v4l2_m2m_get_curr_priv(jpu->m2m_dev); 151362306a36Sopenharmony_ci if (!curr_ctx) { 151462306a36Sopenharmony_ci /* ...instance is not running */ 151562306a36Sopenharmony_ci dev_err(jpu->dev, "no active context for m2m\n"); 151662306a36Sopenharmony_ci goto handled; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); 152062306a36Sopenharmony_ci dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (int_status & JINTS_TRANSF_COMPL) { 152362306a36Sopenharmony_ci if (curr_ctx->encoder) { 152462306a36Sopenharmony_ci unsigned long payload_size = jpu_read(jpu, JCDTCU) << 16 152562306a36Sopenharmony_ci | jpu_read(jpu, JCDTCM) << 8 152662306a36Sopenharmony_ci | jpu_read(jpu, JCDTCD); 152762306a36Sopenharmony_ci vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 152862306a36Sopenharmony_ci payload_size + JPU_JPEG_HDR_SIZE); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci dst_buf->field = src_buf->field; 153262306a36Sopenharmony_ci dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; 153362306a36Sopenharmony_ci if (src_buf->flags & V4L2_BUF_FLAG_TIMECODE) 153462306a36Sopenharmony_ci dst_buf->timecode = src_buf->timecode; 153562306a36Sopenharmony_ci dst_buf->flags = src_buf->flags & 153662306a36Sopenharmony_ci (V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME | 153762306a36Sopenharmony_ci V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | 153862306a36Sopenharmony_ci V4L2_BUF_FLAG_TSTAMP_SRC_MASK); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); 154162306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); 154262306a36Sopenharmony_ci } else if (int_status & JINTS_ERR) { 154362306a36Sopenharmony_ci unsigned char error = jpu_read(jpu, JCDERR) & JCDERR_MASK; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci dev_dbg(jpu->dev, "processing error: %#X: %s\n", error, 154662306a36Sopenharmony_ci error_to_text[error]); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); 154962306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci jpu->curr = NULL; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* ...reset JPU after completion */ 155562306a36Sopenharmony_ci jpu_write(jpu, JCCMD_SRST, JCCMD); 155662306a36Sopenharmony_ci spin_unlock(&jpu->lock); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci return IRQ_HANDLED; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cihandled: 156362306a36Sopenharmony_ci spin_unlock(&jpu->lock); 156462306a36Sopenharmony_ci return IRQ_HANDLED; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci/* 156862306a36Sopenharmony_ci * ============================================================================ 156962306a36Sopenharmony_ci * Driver basic infrastructure 157062306a36Sopenharmony_ci * ============================================================================ 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_cistatic const struct of_device_id jpu_dt_ids[] = { 157362306a36Sopenharmony_ci { .compatible = "renesas,jpu-r8a7790" }, /* H2 */ 157462306a36Sopenharmony_ci { .compatible = "renesas,jpu-r8a7791" }, /* M2-W */ 157562306a36Sopenharmony_ci { .compatible = "renesas,jpu-r8a7792" }, /* V2H */ 157662306a36Sopenharmony_ci { .compatible = "renesas,jpu-r8a7793" }, /* M2-N */ 157762306a36Sopenharmony_ci { .compatible = "renesas,rcar-gen2-jpu" }, 157862306a36Sopenharmony_ci { }, 157962306a36Sopenharmony_ci}; 158062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, jpu_dt_ids); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cistatic int jpu_probe(struct platform_device *pdev) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci struct jpu *jpu; 158562306a36Sopenharmony_ci int ret; 158662306a36Sopenharmony_ci unsigned int i; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci jpu = devm_kzalloc(&pdev->dev, sizeof(*jpu), GFP_KERNEL); 158962306a36Sopenharmony_ci if (!jpu) 159062306a36Sopenharmony_ci return -ENOMEM; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci mutex_init(&jpu->mutex); 159362306a36Sopenharmony_ci spin_lock_init(&jpu->lock); 159462306a36Sopenharmony_ci jpu->dev = &pdev->dev; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* memory-mapped registers */ 159762306a36Sopenharmony_ci jpu->regs = devm_platform_ioremap_resource(pdev, 0); 159862306a36Sopenharmony_ci if (IS_ERR(jpu->regs)) 159962306a36Sopenharmony_ci return PTR_ERR(jpu->regs); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci /* interrupt service routine registration */ 160262306a36Sopenharmony_ci jpu->irq = ret = platform_get_irq(pdev, 0); 160362306a36Sopenharmony_ci if (ret < 0) 160462306a36Sopenharmony_ci return ret; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0, 160762306a36Sopenharmony_ci dev_name(&pdev->dev), jpu); 160862306a36Sopenharmony_ci if (ret) { 160962306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpu->irq); 161062306a36Sopenharmony_ci return ret; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* clocks */ 161462306a36Sopenharmony_ci jpu->clk = devm_clk_get(&pdev->dev, NULL); 161562306a36Sopenharmony_ci if (IS_ERR(jpu->clk)) { 161662306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot get clock\n"); 161762306a36Sopenharmony_ci return PTR_ERR(jpu->clk); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* v4l2 device */ 162162306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &jpu->v4l2_dev); 162262306a36Sopenharmony_ci if (ret) { 162362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register v4l2 device\n"); 162462306a36Sopenharmony_ci return ret; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci /* mem2mem device */ 162862306a36Sopenharmony_ci jpu->m2m_dev = v4l2_m2m_init(&jpu_m2m_ops); 162962306a36Sopenharmony_ci if (IS_ERR(jpu->m2m_dev)) { 163062306a36Sopenharmony_ci v4l2_err(&jpu->v4l2_dev, "Failed to init mem2mem device\n"); 163162306a36Sopenharmony_ci ret = PTR_ERR(jpu->m2m_dev); 163262306a36Sopenharmony_ci goto device_register_rollback; 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* fill in quantization and Huffman tables for encoder */ 163662306a36Sopenharmony_ci for (i = 0; i < JPU_MAX_QUALITY; i++) 163762306a36Sopenharmony_ci jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci strscpy(jpu->vfd_encoder.name, DRV_NAME, sizeof(jpu->vfd_encoder.name)); 164062306a36Sopenharmony_ci jpu->vfd_encoder.fops = &jpu_fops; 164162306a36Sopenharmony_ci jpu->vfd_encoder.ioctl_ops = &jpu_ioctl_ops; 164262306a36Sopenharmony_ci jpu->vfd_encoder.minor = -1; 164362306a36Sopenharmony_ci jpu->vfd_encoder.release = video_device_release_empty; 164462306a36Sopenharmony_ci jpu->vfd_encoder.lock = &jpu->mutex; 164562306a36Sopenharmony_ci jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev; 164662306a36Sopenharmony_ci jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M; 164762306a36Sopenharmony_ci jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING | 164862306a36Sopenharmony_ci V4L2_CAP_VIDEO_M2M_MPLANE; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_VIDEO, -1); 165162306a36Sopenharmony_ci if (ret) { 165262306a36Sopenharmony_ci v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); 165362306a36Sopenharmony_ci goto m2m_init_rollback; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci video_set_drvdata(&jpu->vfd_encoder, jpu); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci strscpy(jpu->vfd_decoder.name, DRV_NAME, sizeof(jpu->vfd_decoder.name)); 165962306a36Sopenharmony_ci jpu->vfd_decoder.fops = &jpu_fops; 166062306a36Sopenharmony_ci jpu->vfd_decoder.ioctl_ops = &jpu_ioctl_ops; 166162306a36Sopenharmony_ci jpu->vfd_decoder.minor = -1; 166262306a36Sopenharmony_ci jpu->vfd_decoder.release = video_device_release_empty; 166362306a36Sopenharmony_ci jpu->vfd_decoder.lock = &jpu->mutex; 166462306a36Sopenharmony_ci jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev; 166562306a36Sopenharmony_ci jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M; 166662306a36Sopenharmony_ci jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING | 166762306a36Sopenharmony_ci V4L2_CAP_VIDEO_M2M_MPLANE; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_VIDEO, -1); 167062306a36Sopenharmony_ci if (ret) { 167162306a36Sopenharmony_ci v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); 167262306a36Sopenharmony_ci goto enc_vdev_register_rollback; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci video_set_drvdata(&jpu->vfd_decoder, jpu); 167662306a36Sopenharmony_ci platform_set_drvdata(pdev, jpu); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci v4l2_info(&jpu->v4l2_dev, "encoder device registered as /dev/video%d\n", 167962306a36Sopenharmony_ci jpu->vfd_encoder.num); 168062306a36Sopenharmony_ci v4l2_info(&jpu->v4l2_dev, "decoder device registered as /dev/video%d\n", 168162306a36Sopenharmony_ci jpu->vfd_decoder.num); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci return 0; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cienc_vdev_register_rollback: 168662306a36Sopenharmony_ci video_unregister_device(&jpu->vfd_encoder); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cim2m_init_rollback: 168962306a36Sopenharmony_ci v4l2_m2m_release(jpu->m2m_dev); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_cidevice_register_rollback: 169262306a36Sopenharmony_ci v4l2_device_unregister(&jpu->v4l2_dev); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci return ret; 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic void jpu_remove(struct platform_device *pdev) 169862306a36Sopenharmony_ci{ 169962306a36Sopenharmony_ci struct jpu *jpu = platform_get_drvdata(pdev); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci video_unregister_device(&jpu->vfd_decoder); 170262306a36Sopenharmony_ci video_unregister_device(&jpu->vfd_encoder); 170362306a36Sopenharmony_ci v4l2_m2m_release(jpu->m2m_dev); 170462306a36Sopenharmony_ci v4l2_device_unregister(&jpu->v4l2_dev); 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 170862306a36Sopenharmony_cistatic int jpu_suspend(struct device *dev) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct jpu *jpu = dev_get_drvdata(dev); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (jpu->ref_count == 0) 171362306a36Sopenharmony_ci return 0; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci clk_disable_unprepare(jpu->clk); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int jpu_resume(struct device *dev) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct jpu *jpu = dev_get_drvdata(dev); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci if (jpu->ref_count == 0) 172562306a36Sopenharmony_ci return 0; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci clk_prepare_enable(jpu->clk); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return 0; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci#endif 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic const struct dev_pm_ops jpu_pm_ops = { 173462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume) 173562306a36Sopenharmony_ci}; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic struct platform_driver jpu_driver = { 173862306a36Sopenharmony_ci .probe = jpu_probe, 173962306a36Sopenharmony_ci .remove_new = jpu_remove, 174062306a36Sopenharmony_ci .driver = { 174162306a36Sopenharmony_ci .of_match_table = jpu_dt_ids, 174262306a36Sopenharmony_ci .name = DRV_NAME, 174362306a36Sopenharmony_ci .pm = &jpu_pm_ops, 174462306a36Sopenharmony_ci }, 174562306a36Sopenharmony_ci}; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cimodule_platform_driver(jpu_driver); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 175062306a36Sopenharmony_ciMODULE_AUTHOR("Mikhail Ulianov <mikhail.ulyanov@cogentembedded.com>"); 175162306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas R-Car JPEG processing unit driver"); 175262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1753