Commit a1e29404 authored by Devarsh Thakkar's avatar Devarsh Thakkar Committed by Hans Verkuil

media: imagination: Add E5010 JPEG Encoder driver

This adds support for stateful V4L2 M2M based driver for Imagination E5010
JPEG Encoder [1] which supports baseline encoding with two different
quantization tables and compression ratio as demanded.

Support for both contiguous and non-contiguous YUV420 and YUV422 semiplanar
formats is added along with alignment restrictions as required by the
hardware.

System and runtime PM hooks are added in the driver along with v4l2 crop
and selection API support.
Minimum resolution supported is 64x64 and
Maximum resolution supported is 8192x8192.

All v4l2-compliance tests are passing [2] :
v4l2-compliance -s -f -a  -d /dev/video0 -e /dev/video1

Total for e5010 device /dev/video0: 79, Succeeded: 79, Failed: 0,
Warnings: 0

NOTE: video1 here is VIVID test pattern generator

Also tests [3] were run manually to verify below driver features:
 - Runtime Power Management
 - Multi-instance JPEG Encoding
 - DMABUF import, export support
 - NV12, NV21, NV16, NV61 video format support
 - Compression quality S_CTRL
 - Cropping support using S_SELECTION

Existing V4L2 M2M based JPEG drivers namely s5p-jpeg, imx-jpeg and rcar_jpu
were referred while making this.

TODO:
Add MMU and memory tiling support

[1]:  AM62A TRM (Section 7.6 is for JPEG Encoder) :
Link: https://www.ti.com/lit/pdf/spruj16

[2]: v4l2-compliance test :
Link: https://gist.github.com/devarsht/1f039c631ca953a57f405cfce1b69e49

[3]: E5010 JPEG Encoder Manual tests :

Performance:
Link: https://gist.github.com/devarsht/c40672944fd71c9a53ab55adbfd9e28b

Functionality:
Link: https://gist.github.com/devarsht/8e88fcaabff016bb2bac83d89c9d23ce

Compression Quality:
Link: https://gist.github.com/devarsht/cbcc7cd97e8c48ba1486caa2b7884655

Multi Instance:
Link: https://gist.github.com/devarsht/22c2fca08cd3441fb40f2c7a4cebc95a

Crop support:
Link: https://gist.github.com/devarsht/de6f5142f678bb1a5338abfd9f814abd

Runtime PM:
Link: https://gist.github.com/devarsht/70cd95d4440ddc678489d93885ddd4ddCo-developed-by: default avatarDavid Huang <d-huang@ti.com>
Signed-off-by: default avatarDavid Huang <d-huang@ti.com>
Signed-off-by: default avatarDevarsh Thakkar <devarsht@ti.com>
Reviewed-by: default avatarBenjamin Gaignard <benjamin.gaignard@collabora.com>
Signed-off-by: default avatarSebastian Fricke <sebastian.fricke@collabora.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
parent dacd54eb
......@@ -10767,8 +10767,10 @@ F: drivers/auxdisplay/img-ascii-lcd.c
IMGTEC JPEG ENCODER DRIVER
M: Devarsh Thakkar <devarsht@ti.com>
L: linux-media@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml
F: drivers/media/platform/imagination/e5010*
IMGTEC IR DECODER DRIVER
S: Orphan
......
......@@ -70,6 +70,7 @@ source "drivers/media/platform/atmel/Kconfig"
source "drivers/media/platform/broadcom/Kconfig"
source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/chips-media/Kconfig"
source "drivers/media/platform/imagination/Kconfig"
source "drivers/media/platform/intel/Kconfig"
source "drivers/media/platform/marvell/Kconfig"
source "drivers/media/platform/mediatek/Kconfig"
......
......@@ -13,6 +13,7 @@ obj-y += atmel/
obj-y += broadcom/
obj-y += cadence/
obj-y += chips-media/
obj-y += imagination/
obj-y += intel/
obj-y += marvell/
obj-y += mediatek/
......
# SPDX-License-Identifier: GPL-2.0
config VIDEO_E5010_JPEG_ENC
tristate "Imagination E5010 JPEG Encoder Driver"
depends on VIDEO_DEV
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
help
This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder,
which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420
semiplanar video formats, with resolution ranging from 64x64 to 8K x 8K
pixels. The module will be named as e5010_jpeg_enc.
# SPDX-License-Identifier: GPL-2.0
e5010_jpeg_enc-objs := e5010-jpeg-enc-hw.o e5010-jpeg-enc.o
obj-$(CONFIG_VIDEO_E5010_JPEG_ENC) += e5010_jpeg_enc.o
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Imagination E5010 JPEG Encoder driver.
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
*
* Author: David Huang <d-huang@ti.com>
* Author: Devarsh Thakkar <devarsht@ti.com>
*/
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/dev_printk.h>
#include "e5010-jpeg-enc-hw.h"
static void write_reg_field(void __iomem *base, unsigned int offset, u32 mask,
unsigned int shift, u32 value)
{
u32 reg;
value <<= shift;
if (mask != 0xffffffff) {
reg = readl(base + offset);
value = (value & mask) | (reg & ~mask);
}
writel(value, (base + offset));
}
static int write_reg_field_not_busy(void __iomem *jasper_base, void __iomem *wr_base,
unsigned int offset, u32 mask, unsigned int shift,
u32 value)
{
int ret;
u32 val;
ret = readl_poll_timeout_atomic(jasper_base + JASPER_STATUS_OFFSET, val,
(val & JASPER_STATUS_CR_JASPER_BUSY_MASK) == 0,
2000, 50000);
if (ret)
return ret;
write_reg_field(wr_base, offset, mask, shift, value);
return 0;
}
void e5010_reset(struct device *dev, void __iomem *core_base, void __iomem *mmu_base)
{
int ret = 0;
u32 val;
write_reg_field(core_base, JASPER_RESET_OFFSET,
JASPER_RESET_CR_CORE_RESET_MASK,
JASPER_RESET_CR_CORE_RESET_SHIFT, 1);
write_reg_field(mmu_base, MMU_MMU_CONTROL1_OFFSET,
MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK,
MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT, 1);
ret = readl_poll_timeout_atomic(mmu_base + MMU_MMU_CONTROL1_OFFSET, val,
(val & MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK) == 0,
2000, 50000);
if (ret)
dev_warn(dev, "MMU soft reset timed out, forcing system soft reset\n");
write_reg_field(core_base, JASPER_RESET_OFFSET,
JASPER_RESET_CR_SYS_RESET_MASK,
JASPER_RESET_CR_SYS_RESET_SHIFT, 1);
}
void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable)
{
/* Bypass MMU */
write_reg_field(mmu_base,
MMU_MMU_ADDRESS_CONTROL_OFFSET,
MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK,
MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT,
enable);
}
int e5010_hw_enable_output_address_error_irq(void __iomem *core_base, u32 enable)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_INTERRUPT_MASK_OFFSET,
JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK,
JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT,
enable);
}
bool e5010_hw_pic_done_irq(void __iomem *core_base)
{
u32 reg;
reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET);
return reg & JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK;
}
bool e5010_hw_output_address_irq(void __iomem *core_base)
{
u32 reg;
reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET);
return reg & JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK;
}
int e5010_hw_enable_picture_done_irq(void __iomem *core_base, u32 enable)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_INTERRUPT_MASK_OFFSET,
JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK,
JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT,
enable);
}
int e5010_hw_enable_auto_clock_gating(void __iomem *core_base, u32 enable)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_CLK_CONTROL_OFFSET,
JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK,
JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT,
enable);
}
int e5010_hw_enable_manual_clock_gating(void __iomem *core_base, u32 enable)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_CLK_CONTROL_OFFSET,
JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK,
JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT, 0);
}
int e5010_hw_enable_crc_check(void __iomem *core_base, u32 enable)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_CRC_CTRL_OFFSET,
JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK,
JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT, enable);
}
int e5010_hw_set_input_source_to_memory(void __iomem *core_base, u32 set)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_INPUT_CTRL0_OFFSET,
JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK,
JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT, set);
}
int e5010_hw_set_input_luma_addr(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
INPUT_LUMA_BASE_OFFSET,
INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK, 0, val);
}
int e5010_hw_set_input_chroma_addr(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
INPUT_CHROMA_BASE_OFFSET,
INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK, 0, val);
}
int e5010_hw_set_output_base_addr(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_OUTPUT_BASE_OFFSET,
JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK,
JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT, val);
}
int e5010_hw_set_horizontal_size(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_IMAGE_SIZE_OFFSET,
JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK,
JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT,
val);
}
int e5010_hw_set_vertical_size(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_IMAGE_SIZE_OFFSET,
JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK,
JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT,
val);
}
int e5010_hw_set_luma_stride(void __iomem *core_base, u32 bytesperline)
{
u32 val = bytesperline / 64;
return write_reg_field_not_busy(core_base, core_base,
JASPER_INPUT_CTRL1_OFFSET,
JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK,
JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT,
val);
}
int e5010_hw_set_chroma_stride(void __iomem *core_base, u32 bytesperline)
{
u32 val = bytesperline / 64;
return write_reg_field_not_busy(core_base, core_base,
JASPER_INPUT_CTRL1_OFFSET,
JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK,
JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT,
val);
}
int e5010_hw_set_input_subsampling(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_INPUT_CTRL0_OFFSET,
JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK,
JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT,
val);
}
int e5010_hw_set_chroma_order(void __iomem *core_base, u32 val)
{
return write_reg_field_not_busy(core_base, core_base,
JASPER_INPUT_CTRL0_OFFSET,
JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK,
JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT,
val);
}
void e5010_hw_set_output_max_size(void __iomem *core_base, u32 val)
{
write_reg_field(core_base, JASPER_OUTPUT_MAX_SIZE_OFFSET,
JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK,
JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT,
val);
}
int e5010_hw_set_qpvalue(void __iomem *core_base, u32 offset, u32 val)
{
return write_reg_field_not_busy(core_base, core_base, offset, 0xffffffff, 0, val);
}
void e5010_hw_clear_output_error(void __iomem *core_base, u32 clear)
{
/* Make sure interrupts are clear */
write_reg_field(core_base, JASPER_INTERRUPT_CLEAR_OFFSET,
JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK,
JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT, clear);
}
void e5010_hw_clear_picture_done(void __iomem *core_base, u32 clear)
{
write_reg_field(core_base,
JASPER_INTERRUPT_CLEAR_OFFSET,
JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK,
JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT, clear);
}
int e5010_hw_get_output_size(void __iomem *core_base)
{
return readl(core_base + JASPER_OUTPUT_SIZE_OFFSET);
}
void e5010_hw_encode_start(void __iomem *core_base, u32 start)
{
write_reg_field(core_base, JASPER_CORE_CTRL_OFFSET,
JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK,
JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT, start);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Imagination E5010 JPEG Encoder driver.
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
*
* Author: David Huang <d-huang@ti.com>
* Author: Devarsh Thakkar <devarsht@ti.com>
*/
#ifndef _E5010_JPEG_ENC_HW_H
#define _E5010_JPEG_ENC_HW_H
#include "e5010-core-regs.h"
#include "e5010-mmu-regs.h"
int e5010_hw_enable_output_address_error_irq(void __iomem *core_offset, u32 enable);
int e5010_hw_enable_picture_done_irq(void __iomem *core_offset, u32 enable);
int e5010_hw_enable_auto_clock_gating(void __iomem *core_offset, u32 enable);
int e5010_hw_enable_manual_clock_gating(void __iomem *core_offset, u32 enable);
int e5010_hw_enable_crc_check(void __iomem *core_offset, u32 enable);
int e5010_hw_set_input_source_to_memory(void __iomem *core_offset, u32 set);
int e5010_hw_set_input_luma_addr(void __iomem *core_offset, u32 val);
int e5010_hw_set_input_chroma_addr(void __iomem *core_offset, u32 val);
int e5010_hw_set_output_base_addr(void __iomem *core_offset, u32 val);
int e5010_hw_get_output_size(void __iomem *core_offset);
int e5010_hw_set_horizontal_size(void __iomem *core_offset, u32 val);
int e5010_hw_set_vertical_size(void __iomem *core_offset, u32 val);
int e5010_hw_set_luma_stride(void __iomem *core_offset, u32 bytesperline);
int e5010_hw_set_chroma_stride(void __iomem *core_offset, u32 bytesperline);
int e5010_hw_set_input_subsampling(void __iomem *core_offset, u32 val);
int e5010_hw_set_chroma_order(void __iomem *core_offset, u32 val);
int e5010_hw_set_qpvalue(void __iomem *core_offset, u32 offset, u32 value);
void e5010_reset(struct device *dev, void __iomem *core_offset, void __iomem *mmu_offset);
void e5010_hw_set_output_max_size(void __iomem *core_offset, u32 val);
void e5010_hw_clear_picture_done(void __iomem *core_offset, u32 clear);
void e5010_hw_encode_start(void __iomem *core_offset, u32 start);
void e5010_hw_clear_output_error(void __iomem *core_offset, u32 clear);
void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable);
bool e5010_hw_pic_done_irq(void __iomem *core_base);
bool e5010_hw_output_address_irq(void __iomem *core_base);
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Imagination E5010 JPEG Encoder driver.
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
*
* Author: David Huang <d-huang@ti.com>
* Author: Devarsh Thakkar <devarsht@ti.com>
*/
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#ifndef _E5010_JPEG_ENC_H
#define _E5010_JPEG_ENC_H
#define MAX_PLANES 2
#define HEADER_SIZE 0x025D
#define MIN_DIMENSION 64
#define MAX_DIMENSION 8192
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
#define E5010_MODULE_NAME "e5010"
#define JPEG_MAX_BYTES_PER_PIXEL 2
/* JPEG marker definitions */
#define START_OF_IMAGE 0xFFD8
#define SOF_BASELINE_DCT 0xFFC0
#define END_OF_IMAGE 0xFFD9
#define START_OF_SCAN 0xFFDA
/* Definitions for the huffman table specification in the Marker segment */
#define DHT_MARKER 0xFFC4
#define LH_DC 0x001F
#define LH_AC 0x00B5
/* Definitions for the quantization table specification in the Marker segment */
#define DQT_MARKER 0xFFDB
#define ACMAX 0x03FF
#define DCMAX 0x07FF
/* Length and precision of the quantization table parameters */
#define LQPQ 0x00430
#define QMAX 255
/* Misc JPEG header definitions */
#define UC_NUM_COMP 3
#define PRECISION 8
#define HORZ_SAMPLING_FACTOR (2 << 4)
#define VERT_SAMPLING_FACTOR_422 1
#define VERT_SAMPLING_FACTOR_420 2
#define COMPONENTS_IN_SCAN 3
#define PELS_IN_BLOCK 64
/* Used for Qp table generation */
#define LUMINOSITY 10
#define CONTRAST 1
#define INCREASE 2
#define QP_TABLE_SIZE (8 * 8)
#define QP_TABLE_FIELD_OFFSET 0x04
/*
* vb2 queue structure
* contains queue data information
*
* @fmt: format info
* @width: frame width
* @height: frame height
* @bytesperline: bytes per line in memory
* @size_image: image size in memory
*/
struct e5010_q_data {
struct e5010_fmt *fmt;
u32 width;
u32 height;
u32 width_adjusted;
u32 height_adjusted;
u32 sizeimage[MAX_PLANES];
u32 bytesperline[MAX_PLANES];
u32 sequence;
struct v4l2_rect crop;
bool crop_set;
};
/*
* Driver device structure
* Holds all memory handles and global parameters
* Shared by all instances
*/
struct e5010_dev {
struct device *dev;
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct video_device *vdev;
void __iomem *core_base;
void __iomem *mmu_base;
struct clk *clk;
struct e5010_context *last_context_run;
/* Protect access to device data */
struct mutex mutex;
/* Protect access to hardware*/
spinlock_t hw_lock;
};
/*
* Driver context structure
* One of these exists for every m2m context
* Holds context specific data
*/
struct e5010_context {
struct v4l2_fh fh;
struct e5010_dev *e5010;
struct e5010_q_data out_queue;
struct e5010_q_data cap_queue;
int quality;
bool update_qp;
struct v4l2_ctrl_handler ctrl_handler;
u8 luma_qp[QP_TABLE_SIZE];
u8 chroma_qp[QP_TABLE_SIZE];
};
/*
* Buffer structure
* Contains info for all buffers
*/
struct e5010_buffer {
struct v4l2_m2m_buffer buffer;
};
enum {
CHROMA_ORDER_CB_CR = 0, //UV ordering
CHROMA_ORDER_CR_CB = 1, //VU ordering
};
enum {
SUBSAMPLING_420 = 1,
SUBSAMPLING_422 = 2,
};
/*
* e5010 format structure
* contains format information
*/
struct e5010_fmt {
u32 fourcc;
unsigned int num_planes;
unsigned int type;
u32 subsampling;
u32 chroma_order;
const struct v4l2_frmsize_stepwise frmsize;
};
/*
* struct e5010_ctrl - contains info for each supported v4l2 control
*/
struct e5010_ctrl {
unsigned int cid;
enum v4l2_ctrl_type type;
unsigned char name[32];
int minimum;
int maximum;
int step;
int default_value;
unsigned char compound;
};
#endif
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment