[RFC PATCH 1/8] s5p-fimc: Add Exynos4x12 FIMC-IS driver

Hans Verkuil hverkuil at xs4all.nl
Wed Mar 13 01:27:00 EST 2013


On Mon 11 March 2013 20:44:45 Sylwester Nawrocki wrote:
> This patch adds a set of core files of the Exynos4x12 FIMC-IS
> V4L2 driver. This includes main functionality like allocating
> memory, loading the firmware, FIMC-IS register interface and
> host CPU <-> IS command and error code definitions.
> 
> The driver currently exposes a single subdev named FIMC-IS-ISP,
> which corresponds to the FIMC-IS ISP and DRC IP blocks.
> 
> The FIMC-IS-ISP subdev currently supports only a subset of user
> controls. For other controls we need several extensions at the
> V4L2 API. The supported standard controls are:
> brightness, contrast, saturation, hue, sharpness, 3a_lock,
> exposure_time_absolute, white_balance_auto_preset,
> iso_sensitivity, iso_sensitivity_auto, exposure_metering_mode.
> Signed-off-by: Sylwester Nawrocki <s.nawrocki at samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
> ---

<snip most of the patch>

> +
> +/* Supported manual ISO values */
> +static const s64 iso_qmenu[] = {
> +	50, 100, 200, 400, 800,
> +};
> +
> +static int __ctrl_set_iso(struct fimc_is *is, int value)
> +{
> +	unsigned int idx, iso;
> +
> +	if (value == V4L2_ISO_SENSITIVITY_AUTO) {
> +		__is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0);
> +		return 0;
> +	}
> +	idx = is->isp.ctrls.iso->val;
> +	if (idx >= ARRAY_SIZE(iso_qmenu))
> +		return -EINVAL;
> +
> +	iso = iso_qmenu[idx];
> +	__is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso);
> +	return 0;
> +}
> +
> +static int __ctrl_set_metering(struct fimc_is *is, unsigned int value)
> +{
> +	unsigned int val;
> +
> +	switch (value) {
> +	case V4L2_EXPOSURE_METERING_AVERAGE:
> +		val = ISP_METERING_COMMAND_AVERAGE;
> +		break;
> +	case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
> +		val = ISP_METERING_COMMAND_CENTER;
> +		break;
> +	case V4L2_EXPOSURE_METERING_SPOT:
> +		val = ISP_METERING_COMMAND_SPOT;
> +		break;
> +	case V4L2_EXPOSURE_METERING_MATRIX:
> +		val = ISP_METERING_COMMAND_MATRIX;
> +		break;
> +	default:
> +		return -EINVAL;
> +	};
> +
> +	__is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val);
> +	return 0;
> +}
> +
> +static int __ctrl_set_afc(struct fimc_is *is, int value)
> +{
> +	switch (value) {
> +	case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
> +		__is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0);
> +		break;
> +	case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
> +		__is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50);
> +		break;
> +	case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
> +		__is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60);
> +		break;
> +	case V4L2_CID_POWER_LINE_FREQUENCY_AUTO:
> +		__is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +#if 0
> +static int __ctrl_set_flash_mode(struct fimc_is *is, int value)
> +{
> +	switch (value) {
> +	case IS_FLASH_MODE_OFF:
> +		__is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE, 0);
> +		break;
> +	case IS_FLASH_MODE_AUTO:
> +		__is_set_isp_flash(is, ISP_FLASH_COMMAND_AUTO, 0);
> +		break;
> +	case IS_FLASH_MODE_AUTO_REDEYE:
> +		__is_set_isp_flash(is, ISP_FLASH_COMMAND_AUTO, 1);
> +		break;
> +	case IS_FLASH_MODE_ON:
> +		__is_set_isp_flash(is, ISP_FLASH_COMMAND_MANUALON, 0);
> +		break;
> +	case IS_FLASH_MODE_TORCH:
> +		__is_set_isp_flash(is, ISP_FLASH_COMMAND_TORCH, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static int __ctrl_set_image_effect(struct fimc_is *is, int value)
> +{
> +	/* FIXME: */
> +	__is_set_isp_effect(is, value);
> +	return 0;
> +}
> +
> +static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl);
> +	struct fimc_is *is = fimc_isp_to_is(isp);
> +	bool set_param = true;
> +	int ret = 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_CONTRAST:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_SATURATION:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_SHARPNESS:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_EXPOSURE_ABSOLUTE:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_BRIGHTNESS:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_HUE:
> +		__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE,
> +				    ctrl->val);
> +		break;
> +
> +	case V4L2_CID_EXPOSURE_METERING:
> +		ret = __ctrl_set_metering(is, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
> +		ret = __ctrl_set_white_balance(is, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_3A_LOCK:
> +		ret = __ctrl_set_aewb_lock(is, ctrl);
> +		set_param = false;
> +		break;
> +
> +	case V4L2_CID_ISO_SENSITIVITY_AUTO:
> +		ret = __ctrl_set_iso(is, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_POWER_LINE_FREQUENCY:
> +		ret = __ctrl_set_afc(is, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_COLORFX:
> +		__ctrl_set_image_effect(is, ctrl->val);
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	if (ret < 0) {
> +		v4l2_err(&isp->subdev, "%s() failed, ctrl: %s, val: %d\n",
> +			 __func__, ctrl->name, ctrl->val);
> +		return ret;
> +	}
> +
> +	if (set_param && test_bit(IS_ST_STREAM_ON, &is->state))
> +		return fimc_is_itf_s_param(is, true);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = {
> +	.s_ctrl	= fimc_is_s_ctrl,
> +};
> +
> +int fimc_isp_subdev_create(struct fimc_isp *isp)
> +{
> +	const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops;
> +	struct v4l2_ctrl_handler *handler = &isp->ctrls.handler;
> +	struct v4l2_subdev *sd = &isp->subdev;
> +	struct fimc_isp_ctrls *ctrls = &isp->ctrls;
> +	int ret;
> +
> +	mutex_init(&isp->subdev_lock);
> +
> +	v4l2_subdev_init(sd, &fimc_is_subdev_ops);
> +	sd->grp_id = GRP_ID_FIMC_IS;
> +	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP");
> +
> +	isp->subdev_pads[FIMC_IS_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	isp->subdev_pads[FIMC_IS_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE;
> +	isp->subdev_pads[FIMC_IS_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_init(&sd->entity, FIMC_IS_SD_PADS_NUM,
> +				isp->subdev_pads, 0);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_ctrl_handler_init(handler, 20);
> +
> +	ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION,
> +						-2, 2, 1, 0);
> +	ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS,
> +						-4, 4, 1, 0);
> +	ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST,
> +						-2, 2, 1, 0);
> +	ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS,
> +						-2, 2, 1, 0);
> +	ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE,
> +						-2, 2, 1, 0);
> +
> +	ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops,
> +					V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
> +					8, ~0x14e, V4L2_WHITE_BALANCE_AUTO);
> +
> +	ctrls->exposure = v4l2_ctrl_new_std(handler, ops,
> +					V4L2_CID_EXPOSURE_ABSOLUTE,
> +					-4, 4, 1, 0);
> +
> +	ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops,
> +					V4L2_CID_EXPOSURE_METERING, 3,
> +					~0xf, V4L2_EXPOSURE_METERING_AVERAGE);
> +
> +	v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY,
> +					V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
> +					V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
> +	/* ISO sensitivity */
> +	ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops,
> +			V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0,
> +			V4L2_ISO_SENSITIVITY_AUTO);
> +
> +	ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops,
> +			V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1,
> +			ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu);
> +
> +	ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops,
> +					V4L2_CID_3A_LOCK, 0, 0x3, 0, 0);
> +
> +	/* FIXME: Adjust the enabled controls mask according
> +	   to the ISP capabilities */
> +	v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX,
> +					V4L2_COLORFX_ANTIQUE,
> +					0, V4L2_COLORFX_NONE);
> +	if (handler->error) {
> +		media_entity_cleanup(&sd->entity);
> +		return handler->error;
> +	}
> +
> +	ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE |
> +				  V4L2_CTRL_FLAG_UPDATE;

Why would auto_iso be volatile? I would expect the iso to be volatile
(in which case the 'false' argument below would be 'true'). Also,
v4l2_ctrl_auto_cluster already sets the UPDATE flag.

> +	v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false);
> +
> +	sd->ctrl_handler = handler;
> +	sd->internal_ops = &fimc_is_subdev_internal_ops;
> +	sd->entity.ops = &fimc_is_subdev_media_ops;
> +	v4l2_set_subdevdata(sd, isp);
> +
> +	return 0;
> +}
> +
> +void fimc_isp_subdev_destroy(struct fimc_isp *isp)
> +{
> +	struct v4l2_subdev *sd = &isp->subdev;
> +
> +	v4l2_device_unregister_subdev(sd);
> +	media_entity_cleanup(&sd->entity);
> +	v4l2_ctrl_handler_free(&isp->ctrls.handler);
> +	v4l2_set_subdevdata(sd, NULL);
> +}
> diff --git a/drivers/media/platform/s5p-fimc/fimc-isp.h b/drivers/media/platform/s5p-fimc/fimc-isp.h
> new file mode 100644
> index 0000000..654039e
> --- /dev/null
> +++ b/drivers/media/platform/s5p-fimc/fimc-isp.h
> @@ -0,0 +1,205 @@
> +/*
> + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + *
> + * Authors: Sylwester Nawrocki <s.nawrocki at samsung.com>
> + *          Younghwan Joo <yhwan.joo at samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#ifndef FIMC_ISP_H_
> +#define FIMC_ISP_H_
> +
> +#include <asm/sizes.h>
> +#include <linux/io.h>
> +#include <linux/irqreturn.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/media-entity.h>
> +#include <media/videobuf2-core.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/s5p_fimc.h>
> +
> +#include "fimc-core.h"
> +
> +/* TODO: revisit these constraints */
> +#define FIMC_ISP_SINK_WIDTH_MIN		(16 + 8)
> +#define FIMC_ISP_SINK_HEIGHT_MIN	(12 + 8)
> +#define FIMC_ISP_SOURCE_WIDTH_MIN	8
> +#define FIMC_ISP_SOURC_HEIGHT_MIN	8
> +/* FIXME: below are random numbers... */
> +#define FIMC_ISP_SINK_WIDTH_MAX		(4000 - 16)
> +#define FIMC_ISP_SINK_HEIGHT_MAX	(4000 + 12)
> +#define FIMC_ISP_SOURCE_WIDTH_MAX	4000
> +#define FIMC_ISP_SOURC_HEIGHT_MAX	4000
> +
> +#define FIMC_ISP_NUM_FORMATS		3
> +#define FIMC_IS_REQ_BUFS_MIN		2
> +
> +#define FIMC_IS_SD_PAD_SINK		0
> +#define FIMC_IS_SD_PAD_SRC_FIFO		1
> +#define FIMC_IS_SD_PAD_SRC_DMA		2
> +#define FIMC_IS_SD_PADS_NUM		3
> +#define FIMC_IS_MAX_PLANES		1
> +
> +/**
> + * struct fimc_isp_frame - source/target frame properties
> + * @f_width: full pixel width
> + * @f_height: full pixel height
> + * @rect: crop/composition rectangle
> + */
> +struct fimc_isp_frame {
> +	u16 f_width;
> +	u16 f_height;
> +	struct v4l2_rect rect;
> +};
> +
> +/**
> + * struct fimc_isp_buffer - video buffer structure
> + * @vb: vb2 buffer
> + * @list: list head for the buffers queue
> + * @paddr: precalculated physical address
> + */
> +struct fimc_isp_buffer {
> +	struct vb2_buffer vb;
> +	struct list_head list;
> +	dma_addr_t paddr;
> +};
> +
> +struct fimc_isp_ctrls {
> +	struct v4l2_ctrl_handler handler;
> +	/* Internal mode selection */
> +	struct v4l2_ctrl *scenario;
> +	/* Frame rate */
> +	struct v4l2_ctrl *fps;
> +	/* Touch AF position */
> +	struct v4l2_ctrl *af_position_x;
> +	struct v4l2_ctrl *af_position_y;
> +	/* Auto white balance */
> +	struct v4l2_ctrl *auto_wb;
> +	/* ISO sensitivity */
> +	struct v4l2_ctrl *auto_iso;
> +	struct v4l2_ctrl *iso;

I suggest putting this in an anonymous struct:

	struct { /* Auto ISO control cluster */
		struct v4l2_ctrl *auto_iso;
		struct v4l2_ctrl *iso;
	};

That way you visually emphasize that these belong together and that you
shouldn't move them around.

> +
> +	struct v4l2_ctrl *contrast;
> +	struct v4l2_ctrl *saturation;
> +	struct v4l2_ctrl *sharpness;
> +	/* Auto/manual exposure */
> +	struct v4l2_ctrl *auto_exp;
> +	/* Manual exposure value */
> +	struct v4l2_ctrl *exposure;
> +	/* Adjust - brightness */
> +	struct v4l2_ctrl *brightness;
> +	/* Adjust - hue */
> +	struct v4l2_ctrl *hue;
> +	/* Exposure metering mode */
> +	struct v4l2_ctrl *exp_metering;
> +	/* AFC */
> +	struct v4l2_ctrl *afc;
> +	/* AE/AWB lock/unlock */
> +	struct v4l2_ctrl *aewb_lock;
> +	/* AF */
> +	struct v4l2_ctrl *focus_mode;
> +	/* AF status */
> +	struct v4l2_ctrl *af_status;
> +};
> +
> +/**
> + * struct fimc_isp - fimc isp structure
> + * @pdev: pointer to FIMC-LITE platform device
> + * @variant: variant information for this IP
> + * @v4l2_dev: pointer to top the level v4l2_device
> + * @vfd: video device node
> + * @fh: v4l2 file handle
> + * @alloc_ctx: videobuf2 memory allocator context
> + * @subdev: FIMC-LITE subdev
> + * @vd_pad: media (sink) pad for the capture video node
> + * @subdev_pads: the subdev media pads
> + * @ctrl_handler: v4l2 control handler
> + * @test_pattern: test pattern controls
> + * @index: FIMC-LITE platform device index
> + * @pipeline: video capture pipeline data structure
> + * @slock: spinlock protecting this data structure and the hw registers
> + * @video_lock: mutex serializing video device and the subdev operations
> + * @clock: FIMC-LITE gate clock
> + * @regs: memory mapped io registers
> + * @irq_queue: interrupt handler waitqueue
> + * @fmt: pointer to color format description structure
> + * @payload: image size in bytes (w x h x bpp)
> + * @inp_frame: camera input frame structure
> + * @out_frame: DMA output frame structure
> + * @out_path: output data path (DMA or FIFO)
> + * @source_subdev_grp_id: source subdev group id
> + * @cac_margin_x: horizontal CAC margin in pixels
> + * @cac_margin_y: vertical CAC margin in pixels
> + * @state: driver state flags
> + * @pending_buf_q: pending buffers queue head
> + * @active_buf_q: the queue head of buffers scheduled in hardware
> + * @capture_vb_queue: vb2 buffers queue for ISP capture video node
> + * @active_buf_count: number of video buffers scheduled in hardware
> + * @frame_count: the captured frames counter
> + * @reqbufs_count: the number of buffers requested with REQBUFS ioctl
> + * @ref_count: driver's private reference counter
> + */
> +struct fimc_isp {
> +	struct platform_device		*pdev;
> +	struct fimc_is_variant		*variant;
> +	struct v4l2_device		*v4l2_dev;
> +	struct video_device		vfd;
> +	struct v4l2_fh			fh;
> +	struct vb2_alloc_ctx		*alloc_ctx;
> +	struct v4l2_subdev		subdev;
> +	struct media_pad		vd_pad;
> +	struct media_pad		subdev_pads[FIMC_IS_SD_PADS_NUM];
> +	struct v4l2_mbus_framefmt	subdev_fmt;
> +	struct v4l2_ctrl		*test_pattern;
> +	struct fimc_pipeline		pipeline;
> +	struct fimc_isp_ctrls		ctrls;
> +
> +	u32				index;
> +	struct mutex			video_lock;
> +	struct mutex			subdev_lock;
> +	spinlock_t			slock;
> +
> +	wait_queue_head_t		irq_queue;
> +
> +	const struct fimc_fmt		*video_capture_format;
> +	unsigned long			payload[FIMC_IS_MAX_PLANES];
> +	struct fimc_isp_frame		inp_frame;
> +	struct fimc_isp_frame		out_frame;
> +	enum fimc_datapath		out_path;
> +	unsigned int			source_subdev_grp_id;
> +
> +	unsigned int			cac_margin_x;
> +	unsigned int 			cac_margin_y;
> +
> +	unsigned long			state;
> +	struct list_head		pending_buf_q;
> +	struct list_head		active_buf_q;
> +	struct vb2_queue		capture_vb_queue;
> +	unsigned int			frame_count;
> +	unsigned int			reqbufs_count;
> +	int				ref_count;
> +};
> +
> +#define ctrl_to_fimc_isp(_ctrl) \
> +	container_of(ctrl->handler, struct fimc_isp, ctrls.handler)
> +
> +struct fimc_is;
> +
> +int fimc_isp_subdev_create(struct fimc_isp *isp);
> +void fimc_isp_subdev_destroy(struct fimc_isp *isp);
> +void fimc_isp_irq_handler(struct fimc_is *is);
> +int fimc_is_create_controls(struct fimc_isp *isp);
> +int fimc_is_delete_controls(struct fimc_isp *isp);
> +const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat,
> +					const u32 *mbus_code, int index);
> +#endif /* FIMC_ISP_H_ */
> 

Otherwise this patch looks very clean and I really have no other comments.

Regards,

	Hans


More information about the devicetree-discuss mailing list