text: /*
   * Samsung Exynos5 SoC series FIMC-IS driver
   *
   * exynos5 fimc-is video functions
   *
   * Copyright (c) 2011 Samsung Electronics Co., Ltd
   *
   * 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.
   */
  
  #include "fimc-is-device-ischain.h"
  #include "fimc-is-device-sensor.h"
  #include "fimc-is-subdev-ctrl.h"
  #include "fimc-is-config.h"
  #include "fimc-is-param.h"
  #include "fimc-is-video.h"
  #include "fimc-is-type.h"
  
  static int fimc_is_ischain_3ap_cfg(struct fimc_is_subdev *subdev,
  	struct fimc_is_device_ischain *device,
  	struct fimc_is_frame *frame,
  	struct fimc_is_crop *incrop,
  	struct fimc_is_crop *otcrop,
  	u32 *lindex,
  	u32 *hindex,
  	u32 *indexes)
  {
  	return 0;
  }
  
  static int fimc_is_ischain_3ap_start(struct fimc_is_device_ischain *device,
  	struct fimc_is_subdev *subdev,
  	struct fimc_is_frame *frame,
  	struct fimc_is_queue *queue,
  	struct taa_param *taa_param,
  	struct fimc_is_crop *otcrop,
  	u32 *lindex,
  	u32 *hindex,
  	u32 *indexes)
  {
  	int ret = 0;
  	struct param_dma_output *dma_output;
  	struct fimc_is_module_enum *module;
  	u32 hw_format, hw_bitwidth;
  
  	BUG_ON(!queue);
  	BUG_ON(!queue->framecfg.format);
  
  	hw_format = queue->framecfg.format->hw_format;
  	hw_bitwidth = queue->framecfg.format->hw_bitwidth; /* memory width per pixel */
  
  	ret = fimc_is_sensor_g_module(device->sensor, &module);
  	if (ret) {
  		merr("fimc_is_sensor_g_module is fail(%d)", device, ret);
  		goto p_err;
  	}
  
  	if ((otcrop->w > taa_param->otf_input.bayer_crop_width) ||
  		(otcrop->h > taa_param->otf_input.bayer_crop_height)) {
  		mrerr("bds output size is invalid((%d, %d) > (%d, %d))", device, frame,
  			otcrop->w,
  			otcrop->h,
  			taa_param->otf_input.bayer_crop_width,
  			taa_param->otf_input.bayer_crop_height);
  		ret = -EINVAL;
  		goto p_err;
  	}
  
  	if (otcrop->x || otcrop->y) {
  		mwarn("crop pos(%d, %d) is ignored", device, otcrop->x, otcrop->y);
  		otcrop->x = 0;
  		otcrop->y = 0;
  	}
  
  	/*
  	 * 3AA BDS ratio limitation on width, height
  	 * ratio = input * 256 / output
  	 * real output = input * 256 / ratio
  	 * real output &= ~1
  	 * real output is same with output crop
  	 */
  	dma_output = fimc_is_itf_g_param(device, frame, subdev->param_dma_ot);
  	dma_output->cmd = DMA_OUTPUT_COMMAND_ENABLE;
  	dma_output->format = hw_format;
  	dma_output->bitwidth = hw_bitwidth;
  	dma_output->msb = ((module->bitwidth < hw_bitwidth) ?  module->bitwidth : hw_bitwidth) - 1;
  	dma_output->width = otcrop->w;
  	dma_output->height = otcrop->h;
  	dma_output->dma_crop_offset_x = otcrop->x;
  	dma_output->dma_crop_offset_x = otcrop->y;
  	dma_output->dma_crop_width = otcrop->w;
  	dma_output->dma_crop_height = otcrop->h;
  	*lindex |= LOWBIT_OF(subdev->param_dma_ot);
  	*hindex |= HIGHBIT_OF(subdev->param_dma_ot);
  	(*indexes)++;
  
  	subdev->output.crop = *otcrop;
  
  	set_bit(FIMC_IS_SUBDEV_RUN, &subdev->state);
  
  p_err:
  	return ret;
  }
  
  static int fimc_is_ischain_3ap_stop(struct fimc_is_device_ischain *device,
  	struct fimc_is_subdev *subdev,
  	struct fimc_is_frame *frame,
  	struct taa_param *taa_param,
  	struct fimc_is_crop *otcrop,
  	u32 *lindex,
  	u32 *hindex,
  	u32 *indexes)
  {
  	int ret = 0;
  	struct param_dma_output *dma_output;
  
  	mdbgd_ischain("%s\n", device, __func__);
  
  	if ((otcrop->w > taa_param->otf_input.bayer_crop_width) ||
  		(otcrop->h > taa_param->otf_input.bayer_crop_height)) {
  		mrerr("bds output size is invalid((%d, %d) > (%d, %d))", device, frame,
  			otcrop->w,
  			otcrop->h,
  			taa_param->otf_input.bayer_crop_width,
  			taa_param->otf_input.bayer_crop_height);
  		ret = -EINVAL;
  		goto p_err;
  	}
  
  	if (otcrop->x || otcrop->y) {
  		mwarn("crop pos(%d, %d) is ignored", device, otcrop->x, otcrop->y);
  		otcrop->x = 0;
  		otcrop->y = 0;
  	}
  
  	dma_output = fimc_is_itf_g_param(device, frame, subdev->param_dma_ot);
  	dma_output->cmd = DMA_OUTPUT_COMMAND_DISABLE;
  	dma_output->width = otcrop->w;
  	dma_output->height = otcrop->h;
  	dma_output->dma_crop_offset_x = otcrop->x;
  	dma_output->dma_crop_offset_x = otcrop->y;
  	dma_output->dma_crop_width = otcrop->w;
  	dma_output->dma_crop_height = otcrop->h;
  	*lindex |= LOWBIT_OF(subdev->param_dma_ot);
  	*hindex |= HIGHBIT_OF(subdev->param_dma_ot);
  	(*indexes)++;
  
  	clear_bit(FIMC_IS_SUBDEV_RUN, &subdev->state);
  
  p_err:
  	return ret;
  }
  
  static int fimc_is_ischain_3ap_tag(struct fimc_is_subdev *subdev,
  	struct fimc_is_device_ischain *device,
  	struct fimc_is_frame *ldr_frame,
  	struct camera2_node *node)
  {
  	int ret = 0;
  	struct fimc_is_subdev *leader;
  	struct fimc_is_queue *queue;
  	struct camera2_scaler_uctl *scalerUd;
  	struct taa_param *taa_param;
  	struct fimc_is_crop *otcrop, otparm;
  	u32 lindex, hindex, indexes;
  	u32 pixelformat = 0;
  
  	BUG_ON(!device);
  	BUG_ON(!device->is_region);
  	BUG_ON(!subdev);
  	BUG_ON(!GET_SUBDEV_QUEUE(subdev));
  	BUG_ON(!ldr_frame);
  	BUG_ON(!ldr_frame->shot);
  	BUG_ON(!node);
  
  #ifdef DBG_STREAMING
  	mdbgd_ischain("3AAP TAG(request %d)\n", device, node->request);
  #endif
  
  	lindex = hindex = indexes = 0;
  	leader = subdev->leader;
  	taa_param = &device->is_region->parameter.taa;
  	scalerUd = &ldr_frame->shot->uctl.scalerUd;
  	queue = GET_SUBDEV_QUEUE(subdev);
  	if (!queue) {
  		merr("queue is NULL", device);
  		ret = -EINVAL;
  		goto p_err;
  	}
  
  	if (!queue->framecfg.format) {
  		merr("format is NULL", device);
  		ret = -EINVAL;
  		goto p_err;
  	}
  
  	pixelformat = queue->framecfg.format->pixelformat;
  	otcrop = (struct fimc_is_crop *)node->output.cropRegion;
  
  	otparm.x = 0;
  	otparm.y = 0;
  	otparm.w = taa_param->vdma2_output.width;
  	otparm.h = taa_param->vdma2_output.height;
  
  	if (IS_NULL_CROP(otcrop))
  		*otcrop = otparm;
  
  	if (node->request) {
  		if (!COMPARE_CROP(otcrop, &otparm) ||
  			!test_bit(FIMC_IS_SUBDEV_RUN, &subdev->state) ||
  			test_bit(FIMC_IS_SUBDEV_FORCE_SET, &leader->state)) {
  			ret = fimc_is_ischain_3ap_start(device,
  				subdev,
  				ldr_frame,
  				queue,
  				taa_param,
  				otcrop,
  				&lindex,
  				&hindex,
  				&indexes);
  			if (ret) {
  				merr("fimc_is_ischain_3ap_start is fail(%d)", device, ret);
  				goto p_err;
  			}
  
  			mdbg_pframe("ot_crop[%d, %d, %d, %d] on\n", device, subdev, ldr_frame,
  				otcrop->x, otcrop->y, otcrop->w, otcrop->h);
  		}
  
  		ret = fimc_is_ischain_buf_tag(device,
  			subdev,
  			ldr_frame,
  			pixelformat,
  			otcrop->w,
  			otcrop->h,
  			scalerUd->txpTargetAddress);
  		if (ret) {
  			mswarn("%d frame is drop", device, subdev, ldr_frame->fcount);
  			node->request = 0;
  		}
  	} else {
  		if (!COMPARE_CROP(otcrop, &otparm) ||
  			test_bit(FIMC_IS_SUBDEV_RUN, &subdev->state) ||
  			test_bit(FIMC_IS_SUBDEV_FORCE_SET, &leader->state)) {
  			ret = fimc_is_ischain_3ap_stop(device,
  				subdev,
  				ldr_frame,
  				taa_param,
  				otcrop,
  				&lindex,
  				&hindex,
  				&indexes);
  			if (ret) {
  				merr("fimc_is_ischain_3ap_stop is fail(%d)", device, ret);
  				goto p_err;
  			}
  
  			mdbg_pframe("ot_crop[%d, %d, %d, %d] off\n", device, subdev, ldr_frame,
  				otcrop->x, otcrop->y, otcrop->w, otcrop->h);
  		}
  
  		scalerUd->txpTargetAddress[0] = 0;
  		scalerUd->txpTargetAddress[1] = 0;
  		scalerUd->txpTargetAddress[2] = 0;
  		node->request = 0;
  	}
  
  	ret = fimc_is_itf_s_param(device, ldr_frame, lindex, hindex, indexes);
  	if (ret) {
  		mrerr("fimc_is_itf_s_param is fail(%d)", device, ldr_frame, ret);
  		goto p_err;
  	}
  
  p_err:
  	return ret;
  }
  
  const struct fimc_is_subdev_ops fimc_is_subdev_3ap_ops = {
  	.bypass			= NULL,
  	.cfg			= fimc_is_ischain_3ap_cfg,
  	.tag			= fimc_is_ischain_3ap_tag,
  };
  