text: // SPDX-License-Identifier: GPL-2.0
  /*
   * Support for Intel Camera Imaging ISP subsystem.
   * Copyright (c) 2015, Intel Corporation.
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   * more details.
   */
  
  #include "ia_css_vf.host.h"
  #include <assert_support.h>
  #include <ia_css_err.h>
  #include <ia_css_frame.h>
  #include <ia_css_frame_public.h>
  #include <ia_css_pipeline.h>
  #define IA_CSS_INCLUDE_CONFIGURATIONS
  #include "ia_css_isp_configs.h"
  
  #include "isp.h"
  
  void
  ia_css_vf_config(
      struct sh_css_isp_vf_isp_config      *to,
      const struct ia_css_vf_configuration *from,
      unsigned int size)
  {
  	unsigned int elems_a = ISP_VEC_NELEMS;
  
  	(void)size;
  	to->vf_downscale_bits = from->vf_downscale_bits;
  	to->enable = from->info != NULL;
  
  	if (from->info) {
  		ia_css_frame_info_to_frame_sp_info(&to->info, from->info);
  		ia_css_dma_configure_from_info(&to->dma.port_b, from->info);
  		to->dma.width_a_over_b = elems_a / to->dma.port_b.elems;
  
  		/* Assume divisiblity here, may need to generalize to fixed point. */
  		assert(elems_a % to->dma.port_b.elems == 0);
  	}
  }
  
  /* compute the log2 of the downscale factor needed to get closest
   * to the requested viewfinder resolution on the upper side. The output cannot
   * be smaller than the requested viewfinder resolution.
   */
  int
  sh_css_vf_downscale_log2(
      const struct ia_css_frame_info *out_info,
      const struct ia_css_frame_info *vf_info,
      unsigned int *downscale_log2) {
  	unsigned int ds_log2 = 0;
  	unsigned int out_width;
  
  	if ((!out_info) | (!vf_info))
  		return -EINVAL;
  
  	out_width = out_info->res.width;
  
  	if (out_width == 0)
  		return -EINVAL;
  
  	/* downscale until width smaller than the viewfinder width. We don't
  	* test for the height since the vmem buffers only put restrictions on
  	* the width of a line, not on the number of lines in a frame.
  	*/
  	while (out_width >= vf_info->res.width)
  	{
  		ds_log2++;
  		out_width /= 2;
  	}
  	/* now width is smaller, so we go up one step */
  	if ((ds_log2 > 0) && (out_width < ia_css_binary_max_vf_width()))
  		ds_log2--;
  	/* TODO: use actual max input resolution of vf_pp binary */
  	if ((out_info->res.width >> ds_log2) >= 2 * ia_css_binary_max_vf_width())
  		return -EINVAL;
  	*downscale_log2 = ds_log2;
  	return 0;
  }
  
  static int
  configure_kernel(
      const struct ia_css_binary_info *info,
      const struct ia_css_frame_info *out_info,
      const struct ia_css_frame_info *vf_info,
      unsigned int *downscale_log2,
      struct ia_css_vf_configuration *config) {
  	int err;
  	unsigned int vf_log_ds = 0;
  
  	/* First compute value */
  	if (vf_info)
  	{
  		err = sh_css_vf_downscale_log2(out_info, vf_info, &vf_log_ds);
  		if (err)
  			return err;
  	}
  	vf_log_ds = min(vf_log_ds, info->vf_dec.max_log_downscale);
  	*downscale_log2 = vf_log_ds;
  
  	/* Then store it in isp config section */
  	config->vf_downscale_bits = vf_log_ds;
  	return 0;
  }
  
  static void
  configure_dma(
      struct ia_css_vf_configuration *config,
      const struct ia_css_frame_info *vf_info)
  {
  	config->info = vf_info;
  }
  
  int
  ia_css_vf_configure(
      const struct ia_css_binary *binary,
      const struct ia_css_frame_info *out_info,
      struct ia_css_frame_info *vf_info,
      unsigned int *downscale_log2) {
  	int err;
  	struct ia_css_vf_configuration config;
  	const struct ia_css_binary_info *info = &binary->info->sp;
  
  	err = configure_kernel(info, out_info, vf_info, downscale_log2, &config);
  	configure_dma(&config, vf_info);
  
  	if (vf_info)
  		vf_info->raw_bit_depth = info->dma.vfdec_bits_per_pixel;
  	ia_css_configure_vf(binary, &config);
  
  	return 0;
  }
  