Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09e231b3 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (11024): soc-camera: separate S_FMT and S_CROP operations



As host and camera drivers become more complex, differences between S_FMT and
S_CROP functionality grow, this patch separates them.

Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 1cd3c0fa
Loading
Loading
Loading
Loading
+17 −2
Original line number Diff line number Diff line
@@ -284,8 +284,8 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
	return soc_camera_apply_sensor_flags(icl, flags);
}

static int mt9m001_set_fmt(struct soc_camera_device *icd,
			   __u32 pixfmt, struct v4l2_rect *rect)
static int mt9m001_set_crop(struct soc_camera_device *icd,
			    struct v4l2_rect *rect)
{
	struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
	int ret;
@@ -324,6 +324,20 @@ static int mt9m001_set_fmt(struct soc_camera_device *icd,
	return ret;
}

static int mt9m001_set_fmt(struct soc_camera_device *icd,
			   struct v4l2_format *f)
{
	struct v4l2_rect rect = {
		.left	= icd->x_current,
		.top	= icd->y_current,
		.width	= f->fmt.pix.width,
		.height	= f->fmt.pix.height,
	};

	/* No support for scaling so far, just crop. TODO: use skipping */
	return mt9m001_set_crop(icd, &rect);
}

static int mt9m001_try_fmt(struct soc_camera_device *icd,
			   struct v4l2_format *f)
{
@@ -449,6 +463,7 @@ static struct soc_camera_ops mt9m001_ops = {
	.release		= mt9m001_release,
	.start_capture		= mt9m001_start_capture,
	.stop_capture		= mt9m001_stop_capture,
	.set_crop		= mt9m001_set_crop,
	.set_fmt		= mt9m001_set_fmt,
	.try_fmt		= mt9m001_try_fmt,
	.set_bus_param		= mt9m001_set_bus_param,
+39 −17
Original line number Diff line number Diff line
@@ -152,7 +152,7 @@ struct mt9m111 {
	struct soc_camera_device icd;
	int model;	/* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
	enum mt9m111_context context;
	unsigned int left, top, width, height;
	struct v4l2_rect rect;
	u32 pixfmt;
	unsigned char autoexposure;
	unsigned char datawidth;
@@ -249,12 +249,13 @@ static int mt9m111_set_context(struct soc_camera_device *icd,
		return reg_write(CONTEXT_CONTROL, valA);
}

static int mt9m111_setup_rect(struct soc_camera_device *icd)
static int mt9m111_setup_rect(struct soc_camera_device *icd,
			      struct v4l2_rect *rect)
{
	struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
	int ret, is_raw_format;
	int width = mt9m111->width;
	int height = mt9m111->height;
	int width = rect->width;
	int height = rect->height;

	if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8)
	    || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16))
@@ -262,9 +263,9 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd)
	else
		is_raw_format = 0;

	ret = reg_write(COLUMN_START, mt9m111->left);
	ret = reg_write(COLUMN_START, rect->left);
	if (!ret)
		ret = reg_write(ROW_START, mt9m111->top);
		ret = reg_write(ROW_START, rect->top);

	if (is_raw_format) {
		if (!ret)
@@ -436,6 +437,22 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
	return 0;
}

static int mt9m111_set_crop(struct soc_camera_device *icd,
			    struct v4l2_rect *rect)
{
	struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
	int ret;

	dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
		__func__, rect->left, rect->top, rect->width,
		rect->height);

	ret = mt9m111_setup_rect(icd, rect);
	if (!ret)
		mt9m111->rect = *rect;
	return ret;
}

static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
{
	struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
@@ -486,23 +503,27 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
}

static int mt9m111_set_fmt(struct soc_camera_device *icd,
			   __u32 pixfmt, struct v4l2_rect *rect)
			   struct v4l2_format *f)
{
	struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
	struct v4l2_pix_format *pix = &f->fmt.pix;
	struct v4l2_rect rect = {
		.left	= mt9m111->rect.left,
		.top	= mt9m111->rect.top,
		.width	= pix->width,
		.height	= pix->height,
	};
	int ret;

	mt9m111->left = rect->left;
	mt9m111->top = rect->top;
	mt9m111->width = rect->width;
	mt9m111->height = rect->height;

	dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n",
		__func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width,
		mt9m111->height);
		__func__, pix->pixelformat, rect.left, rect.top, rect.width,
		rect.height);

	ret = mt9m111_setup_rect(icd);
	ret = mt9m111_setup_rect(icd, &rect);
	if (!ret)
		ret = mt9m111_set_pixfmt(icd, pix->pixelformat);
	if (!ret)
		ret = mt9m111_set_pixfmt(icd, pixfmt);
		mt9m111->rect = rect;
	return ret;
}

@@ -633,6 +654,7 @@ static struct soc_camera_ops mt9m111_ops = {
	.release		= mt9m111_release,
	.start_capture		= mt9m111_start_capture,
	.stop_capture		= mt9m111_stop_capture,
	.set_crop		= mt9m111_set_crop,
	.set_fmt		= mt9m111_set_fmt,
	.try_fmt		= mt9m111_try_fmt,
	.query_bus_param	= mt9m111_query_bus_param,
@@ -817,7 +839,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd)

	mt9m111_set_context(icd, mt9m111->context);
	mt9m111_set_pixfmt(icd, mt9m111->pixfmt);
	mt9m111_setup_rect(icd);
	mt9m111_setup_rect(icd, &mt9m111->rect);
	mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
	mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
	mt9m111_set_global_gain(icd, icd->gain);
+53 −31
Original line number Diff line number Diff line
@@ -213,36 +213,14 @@ static void recalculate_limits(struct soc_camera_device *icd,
	icd->height_max = MT9T031_MAX_HEIGHT / yskip;
}

static int mt9t031_set_fmt(struct soc_camera_device *icd,
			   __u32 pixfmt, struct v4l2_rect *rect)
static int mt9t031_set_params(struct soc_camera_device *icd,
			      struct v4l2_rect *rect, u16 xskip, u16 yskip)
{
	struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
	int ret;
	u16 xbin, ybin, width, height, left, top;
	const u16 hblank = MT9T031_HORIZONTAL_BLANK,
		vblank = MT9T031_VERTICAL_BLANK;
	u16 xbin, xskip, ybin, yskip, width, height, left, top;

	if (pixfmt) {
		/*
		 * try_fmt has put rectangle within limits.
		 * S_FMT - use binning and skipping for scaling, recalculate
		 * limits, used for cropping
		 */
		/* Is this more optimal than just a division? */
		for (xskip = 8; xskip > 1; xskip--)
			if (rect->width * xskip <= MT9T031_MAX_WIDTH)
				break;

		for (yskip = 8; yskip > 1; yskip--)
			if (rect->height * yskip <= MT9T031_MAX_HEIGHT)
				break;

		recalculate_limits(icd, xskip, yskip);
	} else {
		/* CROP - no change in scaling, or in limits */
		xskip = mt9t031->xskip;
		yskip = mt9t031->yskip;
	}

	/* Make sure we don't exceed sensor limits */
	if (rect->left + rect->width > icd->width_max)
@@ -289,7 +267,7 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd,
	if (ret >= 0)
		ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank);

	if (pixfmt) {
	if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
		/* Binning, skipping */
		if (ret >= 0)
			ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE,
@@ -325,15 +303,58 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd,
		}
	}

	if (!ret && pixfmt) {
	/* Re-enable register update, commit all changes */
	if (ret >= 0)
		ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1);

	return ret < 0 ? ret : 0;
}

static int mt9t031_set_crop(struct soc_camera_device *icd,
			    struct v4l2_rect *rect)
{
	struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);

	/* CROP - no change in scaling, or in limits */
	return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip);
}

static int mt9t031_set_fmt(struct soc_camera_device *icd,
			   struct v4l2_format *f)
{
	struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
	int ret;
	u16 xskip, yskip;
	struct v4l2_rect rect = {
		.left	= icd->x_current,
		.top	= icd->y_current,
		.width	= f->fmt.pix.width,
		.height	= f->fmt.pix.height,
	};

	/*
	 * try_fmt has put rectangle within limits.
	 * S_FMT - use binning and skipping for scaling, recalculate
	 * limits, used for cropping
	 */
	/* Is this more optimal than just a division? */
	for (xskip = 8; xskip > 1; xskip--)
		if (rect.width * xskip <= MT9T031_MAX_WIDTH)
			break;

	for (yskip = 8; yskip > 1; yskip--)
		if (rect.height * yskip <= MT9T031_MAX_HEIGHT)
			break;

	recalculate_limits(icd, xskip, yskip);

	ret = mt9t031_set_params(icd, &rect, xskip, yskip);
	if (!ret) {
		mt9t031->xskip = xskip;
		mt9t031->yskip = yskip;
	}

	/* Re-enable register update, commit all changes */
	reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1);

	return ret < 0 ? ret : 0;
	return ret;
}

static int mt9t031_try_fmt(struct soc_camera_device *icd,
@@ -470,6 +491,7 @@ static struct soc_camera_ops mt9t031_ops = {
	.release		= mt9t031_release,
	.start_capture		= mt9t031_start_capture,
	.stop_capture		= mt9t031_stop_capture,
	.set_crop		= mt9t031_set_crop,
	.set_fmt		= mt9t031_set_fmt,
	.try_fmt		= mt9t031_try_fmt,
	.set_bus_param		= mt9t031_set_bus_param,
+39 −23
Original line number Diff line number Diff line
@@ -340,32 +340,11 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
		width_flag;
}

static int mt9v022_set_fmt(struct soc_camera_device *icd,
			   __u32 pixfmt, struct v4l2_rect *rect)
static int mt9v022_set_crop(struct soc_camera_device *icd,
			    struct v4l2_rect *rect)
{
	struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
	int ret;

	/* The caller provides a supported format, as verified per call to
	 * icd->try_fmt(), datawidth is from our supported format list */
	switch (pixfmt) {
	case V4L2_PIX_FMT_GREY:
	case V4L2_PIX_FMT_Y16:
		if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
			return -EINVAL;
		break;
	case V4L2_PIX_FMT_SBGGR8:
	case V4L2_PIX_FMT_SBGGR16:
		if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
			return -EINVAL;
		break;
	case 0:
		/* No format change, only geometry */
		break;
	default:
		return -EINVAL;
	}

	/* Like in example app. Contradicts the datasheet though */
	ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE);
	if (ret >= 0) {
@@ -403,6 +382,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd,
	return 0;
}

static int mt9v022_set_fmt(struct soc_camera_device *icd,
			   struct v4l2_format *f)
{
	struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
	struct v4l2_pix_format *pix = &f->fmt.pix;
	struct v4l2_rect rect = {
		.left	= icd->x_current,
		.top	= icd->y_current,
		.width	= pix->width,
		.height	= pix->height,
	};

	/* The caller provides a supported format, as verified per call to
	 * icd->try_fmt(), datawidth is from our supported format list */
	switch (pix->pixelformat) {
	case V4L2_PIX_FMT_GREY:
	case V4L2_PIX_FMT_Y16:
		if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
			return -EINVAL;
		break;
	case V4L2_PIX_FMT_SBGGR8:
	case V4L2_PIX_FMT_SBGGR16:
		if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
			return -EINVAL;
		break;
	case 0:
		/* No format change, only geometry */
		break;
	default:
		return -EINVAL;
	}

	/* No support for scaling on this camera, just crop. */
	return mt9v022_set_crop(icd, &rect);
}

static int mt9v022_try_fmt(struct soc_camera_device *icd,
			   struct v4l2_format *f)
{
@@ -544,6 +559,7 @@ static struct soc_camera_ops mt9v022_ops = {
	.release		= mt9v022_release,
	.start_capture		= mt9v022_start_capture,
	.stop_capture		= mt9v022_stop_capture,
	.set_crop		= mt9v022_set_crop,
	.set_fmt		= mt9v022_set_fmt,
	.try_fmt		= mt9v022_try_fmt,
	.set_bus_param		= mt9v022_set_bus_param,
+97 −60
Original line number Diff line number Diff line
@@ -544,16 +544,14 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
}

static bool channel_change_requested(struct soc_camera_device *icd,
				     const struct soc_camera_format_xlate *xlate,
				     __u32 pixfmt, struct v4l2_rect *rect)
				     struct v4l2_rect *rect)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct mx3_camera_dev *mx3_cam = ici->priv;
	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];

	/* So far only one configuration is supported */
	return pixfmt || (ichan && rect->width * rect->height >
			  icd->width * icd->height);
	/* Do buffers have to be re-allocated or channel re-configured? */
	return ichan && rect->width * rect->height > icd->width * icd->height;
}

static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -733,26 +731,34 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
	return formats;
}

static int mx3_camera_set_fmt(struct soc_camera_device *icd,
			      __u32 pixfmt, struct v4l2_rect *rect)
static void configure_geometry(struct mx3_camera_dev *mx3_cam,
			       struct v4l2_rect *rect)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct mx3_camera_dev *mx3_cam = ici->priv;
	const struct soc_camera_format_xlate *xlate;
	u32 ctrl, width_field, height_field;
	int ret;

	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
	if (pixfmt && !xlate) {
		dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
		return -EINVAL;
	}
	/* Setup frame size - this cannot be changed on-the-fly... */
	width_field = rect->width - 1;
	height_field = rect->height - 1;
	csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);

	csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
	csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);

	csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);

	/* ...and position */
	ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
	/* Sensor does the cropping */
	csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);

	/*
	 * We now know pixel formats and can decide upon DMA-channel(s)
	 * So far only direct camera-to-memory is supported
	 * No need to free resources here if we fail, we'll see if we need to
	 * do this next time we are called
	 */
	if (channel_change_requested(icd, xlate, pixfmt, rect)) {
}

static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
{
	dma_cap_mask_t mask;
	struct dma_chan *chan;
	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
@@ -781,36 +787,66 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,

	*ichan = to_idmac_chan(chan);
	(*ichan)->client = mx3_cam;

	return 0;
}

static int mx3_camera_set_crop(struct soc_camera_device *icd,
			       struct v4l2_rect *rect)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct mx3_camera_dev *mx3_cam = ici->priv;

	/*
	 * Might have to perform a complete interface initialisation like in
	 * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
	 * mxc_v4l2_s_fmt()
	 * We now know pixel formats and can decide upon DMA-channel(s)
	 * So far only direct camera-to-memory is supported
	 */
	if (channel_change_requested(icd, rect)) {
		int ret = acquire_dma_channel(mx3_cam);
		if (ret < 0)
			return ret;
	}

	/* Setup frame size - this cannot be changed on-the-fly... */
	width_field = rect->width - 1;
	height_field = rect->height - 1;
	csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
	configure_geometry(mx3_cam, rect);

	csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
	csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
	return icd->ops->set_crop(icd, rect);
}

	csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
static int mx3_camera_set_fmt(struct soc_camera_device *icd,
			      struct v4l2_format *f)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct mx3_camera_dev *mx3_cam = ici->priv;
	const struct soc_camera_format_xlate *xlate;
	struct v4l2_pix_format *pix = &f->fmt.pix;
	struct v4l2_rect rect = {
		.left	= icd->x_current,
		.top	= icd->y_current,
		.width	= pix->width,
		.height	= pix->height,
	};
	int ret;

	/* ...and position */
	ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
	/* Sensor does the cropping */
	csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
	if (!xlate) {
		dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat);
		return -EINVAL;
	}

	ret = acquire_dma_channel(mx3_cam);
	if (ret < 0)
		return ret;

	/*
	 * No need to free resources here if we fail, we'll see if we need to
	 * do this next time we are called
	 * Might have to perform a complete interface initialisation like in
	 * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
	 * mxc_v4l2_s_fmt()
	 */

	ret = icd->ops->set_fmt(icd, pixfmt ? xlate->cam_fmt->fourcc : 0, rect);
	if (pixfmt && !ret) {
	configure_geometry(mx3_cam, &rect);

	ret = icd->ops->set_fmt(icd, f);
	if (!ret) {
		icd->buswidth = xlate->buswidth;
		icd->current_fmt = xlate->host_fmt;
	}
@@ -1031,6 +1067,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
	.suspend	= mx3_camera_suspend,
	.resume		= mx3_camera_resume,
#endif
	.set_crop	= mx3_camera_set_crop,
	.set_fmt	= mx3_camera_set_fmt,
	.try_fmt	= mx3_camera_try_fmt,
	.get_formats	= mx3_camera_get_formats,
Loading