blob: 48b0363af600d4d2ccf8334f91cb188796cfda29 [file] [log] [blame]
/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that 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.
*/
#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include "cam_io_util.h"
#include "cam_cdm_util.h"
#include "cam_hw_intf.h"
#include "cam_vfe_hw_intf.h"
#include "cam_irq_controller.h"
#include "cam_vfe_bus.h"
#include "cam_vfe_bus_ver2.h"
#include "cam_vfe_core.h"
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
static uint32_t irq_reg_offset[CAM_IFE_BUS_IRQ_REGISTERS_MAX] = {
0x0000205C,
0x00002060,
0x00002064,
0x0000206C,
0x00002070,
0x00002074,
0x00002078,
};
enum cam_vfe_bus_packer_format {
PACKER_FMT_PLAIN_128 = 0x0,
PACKER_FMT_PLAIN_8 = 0x1,
PACKER_FMT_PLAIN_16_10BPP = 0x2,
PACKER_FMT_PLAIN_16_12BPP = 0x3,
PACKER_FMT_PLAIN_16_14BPP = 0x4,
PACKER_FMT_PLAIN_16_16BPP = 0x5,
PACKER_FMT_ARGB_10 = 0x6,
PACKER_FMT_ARGB_12 = 0x7,
PACKER_FMT_ARGB_14 = 0x8,
PACKER_FMT_PLAIN_32_20BPP = 0x9,
PACKER_FMT_PLAIN_64 = 0xA,
PACKER_FMT_TP_10 = 0xB,
PACKER_FMT_PLAIN_32_32BPP = 0xC,
PACKER_FMT_PLAIN_8_ODD_EVEN = 0xD,
PACKER_FMT_PLAIN_8_LSB_MSB_10 = 0xE,
PACKER_FMT_PLAIN_8_LSB_MSB_10_ODD_EVEN = 0xF,
PACKER_FMT_MAX = 0xF,
};
struct cam_vfe_bus_ver2_common_data {
void __iomem *mem_base;
struct cam_hw_intf *hw_intf;
void *bus_irq_controller;
void *vfe_irq_controller;
struct cam_vfe_bus_ver2_reg_offset_common *common_reg;
};
struct cam_vfe_bus_ver2_wm_resource_data {
uint32_t index;
struct cam_vfe_bus_ver2_common_data *common_data;
struct cam_vfe_bus_ver2_reg_offset_bus_client *hw_regs;
uint32_t irq_enabled;
uint32_t offset;
uint32_t width;
uint32_t height;
uint32_t stride;
uint32_t format;
enum cam_vfe_bus_packer_format pack_fmt;
uint32_t burst_len;
uint32_t frame_based;
uint32_t irq_subsample_period;
uint32_t irq_subsample_pattern;
uint32_t framedrop_period;
uint32_t framedrop_pattern;
};
struct cam_vfe_bus_ver2_comp_grp_data {
enum cam_vfe_bus_ver2_comp_grp_type comp_grp_type;
struct cam_vfe_bus_ver2_common_data *common_data;
struct cam_vfe_bus_ver2_reg_offset_comp_grp *hw_regs;
uint32_t irq_enabled;
uint32_t comp_grp_local_idx;
uint32_t unique_id;
uint32_t is_master;
uint32_t dual_slave_core;
uint32_t intra_client_mask;
uint32_t composite_mask;
};
struct cam_vfe_bus_ver2_vfe_out_data {
uint32_t out_type;
struct cam_vfe_bus_ver2_common_data *common_data;
uint32_t num_wm;
struct cam_isp_resource_node *wm_res[PLANE_MAX];
struct cam_isp_resource_node *comp_grp;
enum cam_isp_hw_sync_mode dual_comp_sync_mode;
uint32_t dual_hw_alternate_vfe_id;
struct list_head vfe_out_list;
uint32_t format;
uint32_t max_width;
uint32_t max_height;
struct cam_cdm_utils_ops *cdm_util_ops;
};
struct cam_vfe_bus_ver2_priv {
struct cam_vfe_bus_ver2_common_data common_data;
struct cam_isp_resource_node bus_client[CAM_VFE_BUS_VER2_MAX_CLIENTS];
struct cam_isp_resource_node comp_grp[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
struct cam_isp_resource_node vfe_out[CAM_VFE_BUS_VER2_VFE_OUT_MAX];
struct list_head free_comp_grp;
struct list_head free_dual_comp_grp;
struct list_head used_comp_grp;
struct cam_vfe_bus_irq_evt_payload evt_payload[128];
struct list_head free_payload_list;
};
static int cam_vfe_bus_put_evt_payload(void *core_info,
struct cam_vfe_bus_irq_evt_payload **evt_payload);
static int cam_vfe_bus_ver2_get_intra_client_mask(
enum cam_vfe_bus_ver2_vfe_core_id dual_slave_core,
enum cam_vfe_bus_ver2_vfe_core_id current_core,
uint32_t *intra_client_mask)
{
int rc = 0;
*intra_client_mask = 0;
if (dual_slave_core == current_core) {
pr_err("Invalid params. Same core as Master and Slave\n");
return -EINVAL;
}
switch (current_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_0:
switch (dual_slave_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_1:
*intra_client_mask = 0x1;
break;
case CAM_VFE_BUS_VER2_VFE_CORE_2:
*intra_client_mask = 0x2;
break;
default:
pr_err("Invalid value for slave core %u\n",
dual_slave_core);
rc = -EINVAL;
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_CORE_1:
switch (dual_slave_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_0:
*intra_client_mask = 0x1;
break;
case CAM_VFE_BUS_VER2_VFE_CORE_2:
*intra_client_mask = 0x2;
break;
default:
pr_err("Invalid value for slave core %u\n",
dual_slave_core);
rc = -EINVAL;
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_CORE_2:
switch (dual_slave_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_0:
*intra_client_mask = 0x1;
break;
case CAM_VFE_BUS_VER2_VFE_CORE_1:
*intra_client_mask = 0x2;
break;
default:
pr_err("Invalid value for slave core %u\n",
dual_slave_core);
rc = -EINVAL;
break;
}
break;
default:
pr_err("Invalid value for master core %u\n", current_core);
rc = -EINVAL;
break;
}
return rc;
}
static enum cam_vfe_bus_ver2_vfe_out_type
cam_vfe_bus_get_out_res_id(uint32_t res_type)
{
switch (res_type) {
case CAM_ISP_IFE_OUT_RES_FULL:
return CAM_VFE_BUS_VER2_VFE_OUT_FULL;
case CAM_ISP_IFE_OUT_RES_DS4:
return CAM_VFE_BUS_VER2_VFE_OUT_DS4;
case CAM_ISP_IFE_OUT_RES_DS16:
return CAM_VFE_BUS_VER2_VFE_OUT_DS16;
case CAM_ISP_IFE_OUT_RES_FD:
return CAM_VFE_BUS_VER2_VFE_OUT_FD;
case CAM_ISP_IFE_OUT_RES_RAW_DUMP:
return CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP;
case CAM_ISP_IFE_OUT_RES_PDAF:
return CAM_VFE_BUS_VER2_VFE_OUT_PDAF;
case CAM_ISP_IFE_OUT_RES_RDI_0:
return CAM_VFE_BUS_VER2_VFE_OUT_RDI0;
case CAM_ISP_IFE_OUT_RES_RDI_1:
return CAM_VFE_BUS_VER2_VFE_OUT_RDI1;
case CAM_ISP_IFE_OUT_RES_RDI_2:
return CAM_VFE_BUS_VER2_VFE_OUT_RDI2;
case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE;
case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST;
case CAM_ISP_IFE_OUT_RES_STATS_TL_BG:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG;
case CAM_ISP_IFE_OUT_RES_STATS_BF:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF;
case CAM_ISP_IFE_OUT_RES_STATS_AWB_BG:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG;
case CAM_ISP_IFE_OUT_RES_STATS_BHIST:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST;
case CAM_ISP_IFE_OUT_RES_STATS_RS:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS;
case CAM_ISP_IFE_OUT_RES_STATS_CS:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS;
case CAM_ISP_IFE_OUT_RES_STATS_IHIST:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST;
default:
return CAM_VFE_BUS_VER2_VFE_OUT_MAX;
}
}
static int cam_vfe_bus_get_num_wm(
enum cam_vfe_bus_ver2_vfe_out_type res_type,
uint32_t format)
{
switch (res_type) {
case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
switch (format) {
case CAM_FORMAT_MIPI_RAW_8:
case CAM_FORMAT_MIPI_RAW_10:
case CAM_FORMAT_MIPI_RAW_12:
case CAM_FORMAT_MIPI_RAW_14:
case CAM_FORMAT_MIPI_RAW_16:
case CAM_FORMAT_MIPI_RAW_20:
case CAM_FORMAT_PLAIN128:
return 1;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_FULL:
switch (format) {
case CAM_FORMAT_NV21:
case CAM_FORMAT_NV12:
case CAM_FORMAT_MIPI_RAW_8:
case CAM_FORMAT_PLAIN8:
case CAM_FORMAT_TP10:
case CAM_FORMAT_UBWC_NV12:
case CAM_FORMAT_UBWC_NV12_4R:
case CAM_FORMAT_UBWC_TP10:
case CAM_FORMAT_UBWC_P010:
return 2;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_FD:
switch (format) {
case CAM_FORMAT_NV21:
case CAM_FORMAT_NV12:
case CAM_FORMAT_PLAIN8:
case CAM_FORMAT_TP10:
case CAM_FORMAT_PLAIN16_10:
return 2;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_DS4:
case CAM_VFE_BUS_VER2_VFE_OUT_DS16:
switch (format) {
case CAM_FORMAT_PD8:
case CAM_FORMAT_PD10:
return 1;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP:
switch (format) {
case CAM_FORMAT_ARGB_14:
case CAM_FORMAT_PLAIN8:
case CAM_FORMAT_PLAIN16_10:
case CAM_FORMAT_PLAIN16_12:
case CAM_FORMAT_PLAIN16_14:
return 1;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_PDAF:
switch (format) {
case CAM_FORMAT_PLAIN8:
case CAM_FORMAT_PLAIN16_10:
case CAM_FORMAT_PLAIN16_12:
case CAM_FORMAT_PLAIN16_14:
return 1;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS:
switch (format) {
case CAM_FORMAT_PLAIN64:
return 1;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS:
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST:
switch (format) {
case CAM_FORMAT_PLAIN16_16:
return 1;
default:
break;
}
break;
default:
break;
}
pr_err("Unsupported format %u for resource_type %u", format, res_type);
return -EINVAL;
}
static int cam_vfe_bus_get_wm_idx(
enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id,
enum cam_vfe_bus_plane_type plane)
{
int wm_idx = -1;
switch (vfe_out_res_id) {
case CAM_VFE_BUS_VER2_VFE_OUT_FULL:
switch (plane) {
case PLANE_Y:
wm_idx = 3;
break;
case PLANE_C:
wm_idx = 4;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_DS4:
switch (plane) {
case PLANE_Y:
wm_idx = 5;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_DS16:
switch (plane) {
case PLANE_Y:
wm_idx = 6;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_FD:
switch (plane) {
case PLANE_Y:
wm_idx = 7;
break;
case PLANE_C:
wm_idx = 8;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP:
switch (plane) {
case PLANE_Y:
wm_idx = 9;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_PDAF:
switch (plane) {
case PLANE_Y:
wm_idx = 10;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
switch (plane) {
case PLANE_Y:
wm_idx = 0;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
switch (plane) {
case PLANE_Y:
wm_idx = 1;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
switch (plane) {
case PLANE_Y:
wm_idx = 2;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE:
switch (plane) {
case PLANE_Y:
wm_idx = 11;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST:
switch (plane) {
case PLANE_Y:
wm_idx = 12;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG:
switch (plane) {
case PLANE_Y:
wm_idx = 13;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF:
switch (plane) {
case PLANE_Y:
wm_idx = 14;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG:
switch (plane) {
case PLANE_Y:
wm_idx = 15;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST:
switch (plane) {
case PLANE_Y:
wm_idx = 16;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS:
switch (plane) {
case PLANE_Y:
wm_idx = 17;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS:
switch (plane) {
case PLANE_Y:
wm_idx = 18;
break;
default:
break;
}
break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST:
switch (plane) {
case PLANE_Y:
wm_idx = 19;
break;
default:
break;
}
break;
default:
break;
}
return wm_idx;
}
static enum cam_vfe_bus_packer_format
cam_vfe_bus_get_packer_fmt(uint32_t out_fmt)
{
switch (out_fmt) {
case CAM_FORMAT_NV21:
case CAM_FORMAT_NV12:
return PACKER_FMT_PLAIN_8;
default:
return PACKER_FMT_MAX;
}
}
static int cam_vfe_bus_acquire_wm(
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_isp_out_port_info *out_port_info,
enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id,
enum cam_vfe_bus_plane_type plane,
enum cam_isp_hw_split_id split_id,
uint32_t subscribe_irq,
struct cam_isp_resource_node **wm_res,
uint32_t *client_done_mask)
{
uint32_t wm_idx = 0;
struct cam_isp_resource_node *wm_res_local = NULL;
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = NULL;
*wm_res = NULL;
*client_done_mask = 0;
/* No need to allocate for BUS VER2. VFE OUT to WM is fixed. */
wm_idx = cam_vfe_bus_get_wm_idx(vfe_out_res_id, plane);
if (wm_idx < 0 || wm_idx >= CAM_VFE_BUS_VER2_MAX_CLIENTS) {
pr_err("Unsupported VFE out %d plane %d\n",
vfe_out_res_id, plane);
return -EINVAL;
}
wm_res_local = &ver2_bus_priv->bus_client[wm_idx];
wm_res_local->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
rsrc_data = wm_res_local->res_priv;
rsrc_data->irq_enabled = subscribe_irq;
rsrc_data->format = out_port_info->format;
rsrc_data->pack_fmt = cam_vfe_bus_get_packer_fmt(rsrc_data->format);
rsrc_data->width = out_port_info->width;
rsrc_data->height = out_port_info->height;
if (plane == PLANE_C) {
switch (rsrc_data->format) {
case CAM_FORMAT_NV21:
case CAM_FORMAT_NV12:
rsrc_data->height /= 2;
break;
default:
break;
}
}
if (vfe_out_res_id >= CAM_ISP_IFE_OUT_RES_RDI_0 &&
vfe_out_res_id <= CAM_ISP_IFE_OUT_RES_RDI_3)
rsrc_data->frame_based = 1;
*client_done_mask = (1 << wm_idx);
*wm_res = wm_res_local;
return 0;
}
static int cam_vfe_bus_release_wm(void *bus_priv,
struct cam_isp_resource_node *wm_res)
{
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data =
wm_res->res_priv;
rsrc_data->irq_enabled = 0;
rsrc_data->offset = 0;
rsrc_data->width = 0;
rsrc_data->height = 0;
rsrc_data->stride = 0;
rsrc_data->format = 0;
rsrc_data->pack_fmt = 0;
rsrc_data->burst_len = 0;
rsrc_data->frame_based = 0;
rsrc_data->irq_subsample_period = 0;
rsrc_data->irq_subsample_pattern = 0;
rsrc_data->framedrop_period = 0;
rsrc_data->framedrop_pattern = 0;
wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
return 0;
}
static int cam_vfe_bus_start_wm(struct cam_isp_resource_node *wm_res)
{
int rc = 0;
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data =
wm_res->res_priv;
struct cam_vfe_bus_ver2_common_data *common_data =
rsrc_data->common_data;
CDBG("WM res %d width = %d, height = %d\n", rsrc_data->index,
rsrc_data->width, rsrc_data->height);
CDBG("WM res %d pk_fmt = %d\n", rsrc_data->index,
rsrc_data->pack_fmt & PACKER_FMT_MAX);
CDBG("WM res %d stride = %d, burst len = %d\n",
rsrc_data->index, rsrc_data->width, 0xf);
cam_io_w_mb(0,
common_data->mem_base + rsrc_data->hw_regs->header_addr);
cam_io_w_mb(0,
common_data->mem_base + rsrc_data->hw_regs->header_cfg);
cam_io_w_mb(0,
common_data->mem_base + rsrc_data->hw_regs->frame_inc);
cam_io_w_mb(rsrc_data->width,
common_data->mem_base + rsrc_data->hw_regs->buffer_width_cfg);
cam_io_w(rsrc_data->height,
common_data->mem_base + rsrc_data->hw_regs->buffer_height_cfg);
cam_io_w(0xe,
common_data->mem_base + rsrc_data->hw_regs->packer_cfg);
cam_io_w(rsrc_data->width,
common_data->mem_base + rsrc_data->hw_regs->stride);
cam_io_w(0xf,
common_data->mem_base + rsrc_data->hw_regs->burst_limit);
cam_io_w(0xFFFFFFFF, common_data->mem_base +
rsrc_data->hw_regs->irq_subsample_pattern);
cam_io_w(0x0, common_data->mem_base +
rsrc_data->hw_regs->irq_subsample_period);
cam_io_w(0xFFFFFFFF,
common_data->mem_base + rsrc_data->hw_regs->framedrop_pattern);
cam_io_w(0x0,
common_data->mem_base + rsrc_data->hw_regs->framedrop_period);
/* UBWC registers */
switch (rsrc_data->format) {
case CAM_FORMAT_UBWC_NV12:
/* Program UBWC registers */
break;
default:
break;
}
/* Subscribe IRQ */
if (rsrc_data->irq_enabled) {
/*
* Currently all WM IRQ are subscribed in one place. Need to
* make it dynamic later.
*/
}
/* Enable WM */
cam_io_w_mb(0x1,
common_data->mem_base + rsrc_data->hw_regs->cfg);
CDBG("enable WM red %d offset 0x%x val 0x%x\n", rsrc_data->index,
(uint32_t) rsrc_data->hw_regs->cfg, 0x1);
wm_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
return rc;
}
static int cam_vfe_bus_stop_wm(struct cam_isp_resource_node *wm_res)
{
int rc = 0;
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data =
wm_res->res_priv;
struct cam_vfe_bus_ver2_common_data *common_data =
rsrc_data->common_data;
/* Disble WM */
cam_io_w_mb(0x0,
common_data->mem_base + rsrc_data->hw_regs->cfg);
CDBG("irq_enabled %d", rsrc_data->irq_enabled);
/* Unsubscribe IRQ */
if (rsrc_data->irq_enabled) {
/*
* Currently all WM IRQ are unsubscribed in one place. Need to
* make it dynamic.
*/
}
/* Halt & Reset WM */
cam_io_w_mb(BIT(rsrc_data->index),
common_data->mem_base + common_data->common_reg->sw_reset);
wm_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
return rc;
}
static int cam_vfe_bus_handle_wm_done_top_half(uint32_t evt_id,
struct cam_irq_th_payload *th_payload)
{
return -EPERM;
}
static int cam_vfe_bus_handle_wm_done_bottom_half(void *wm_node,
void *evt_payload_priv)
{
int rc = CAM_VFE_IRQ_STATUS_ERR;
struct cam_isp_resource_node *wm_res = wm_node;
struct cam_vfe_bus_irq_evt_payload *evt_payload = evt_payload_priv;
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data = wm_res->res_priv;
uint32_t *cam_ife_irq_regs = evt_payload->irq_reg_val;
uint32_t status_reg;
status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0];
if (status_reg & BIT(rsrc_data->index))
rc = CAM_VFE_IRQ_STATUS_SUCCESS;
if (rc == CAM_VFE_IRQ_STATUS_SUCCESS)
cam_vfe_bus_put_evt_payload(evt_payload->core_info,
&evt_payload);
return rc;
}
static int cam_vfe_bus_init_wm_resource(uint32_t index,
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_vfe_bus_ver2_hw_info *ver2_hw_info,
struct cam_isp_resource_node *wm_res)
{
int rc = 0;
struct cam_vfe_bus_ver2_wm_resource_data *rsrc_data;
rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_wm_resource_data),
GFP_KERNEL);
if (!rsrc_data) {
CDBG("Failed to alloc for wm res priv\n");
rc = -ENOMEM;
return rc;
}
wm_res->res_priv = rsrc_data;
rsrc_data->index = index;
rsrc_data->hw_regs = &ver2_hw_info->bus_client_reg[index];
rsrc_data->common_data = &ver2_bus_priv->common_data;
wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
INIT_LIST_HEAD(&wm_res->list);
wm_res->start = cam_vfe_bus_start_wm;
wm_res->stop = cam_vfe_bus_stop_wm;
wm_res->top_half_handler = cam_vfe_bus_handle_wm_done_top_half;
wm_res->bottom_half_handler = cam_vfe_bus_handle_wm_done_bottom_half;
wm_res->hw_intf = ver2_bus_priv->common_data.hw_intf;
return rc;
}
static void cam_vfe_bus_add_wm_to_comp_grp(
struct cam_isp_resource_node *comp_grp,
uint32_t composite_mask)
{
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = comp_grp->res_priv;
rsrc_data->composite_mask |= composite_mask;
}
static void cam_vfe_bus_match_comp_grp(
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_isp_resource_node **comp_grp,
uint32_t comp_grp_local_idx,
uint32_t unique_id)
{
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL;
struct cam_isp_resource_node *comp_grp_local = NULL;
list_for_each_entry(comp_grp_local,
&ver2_bus_priv->used_comp_grp, list) {
rsrc_data = comp_grp_local->res_priv;
if (rsrc_data->comp_grp_local_idx == comp_grp_local_idx &&
rsrc_data->unique_id == unique_id) {
/* Match found */
*comp_grp = comp_grp_local;
return;
}
}
*comp_grp = NULL;
}
static int cam_vfe_bus_acquire_comp_grp(
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_isp_out_port_info *out_port_info,
uint32_t unique_id,
uint32_t is_dual,
uint32_t is_master,
enum cam_vfe_bus_ver2_vfe_core_id dual_slave_core,
struct cam_isp_resource_node **comp_grp)
{
int rc = 0;
struct cam_isp_resource_node *comp_grp_local = NULL;
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = NULL;
/* Check if matching comp_grp already acquired */
cam_vfe_bus_match_comp_grp(ver2_bus_priv, &comp_grp_local,
out_port_info->comp_grp_id, unique_id);
if (!comp_grp_local) {
/* First find a free group */
if (is_dual) {
if (list_empty(&ver2_bus_priv->free_dual_comp_grp)) {
pr_err("No Free Composite Group\n");
return -ENODEV;
}
comp_grp_local = list_first_entry(
&ver2_bus_priv->free_dual_comp_grp,
struct cam_isp_resource_node, list);
rsrc_data = comp_grp_local->res_priv;
rc = cam_vfe_bus_ver2_get_intra_client_mask(
dual_slave_core,
comp_grp_local->hw_intf->hw_idx,
&rsrc_data->intra_client_mask);
} else {
if (list_empty(&ver2_bus_priv->free_comp_grp)) {
pr_err("No Free Composite Group\n");
return -ENODEV;
}
comp_grp_local = list_first_entry(
&ver2_bus_priv->free_comp_grp,
struct cam_isp_resource_node, list);
rsrc_data = comp_grp_local->res_priv;
}
list_del(&comp_grp_local->list);
comp_grp_local->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
rsrc_data->is_master = is_master;
rsrc_data->composite_mask = 0;
rsrc_data->unique_id = unique_id;
rsrc_data->comp_grp_local_idx = out_port_info->comp_grp_id;
list_add_tail(&comp_grp_local->list,
&ver2_bus_priv->used_comp_grp);
} else {
rsrc_data = comp_grp_local->res_priv;
/* Do not support runtime change in composite mask */
if (comp_grp_local->res_state ==
CAM_ISP_RESOURCE_STATE_STREAMING) {
pr_err("Invalid State %d Comp Grp %u\n",
comp_grp_local->res_state,
rsrc_data->comp_grp_type);
return -EBUSY;
}
}
*comp_grp = comp_grp_local;
return rc;
}
static int cam_vfe_bus_release_comp_grp(
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_isp_resource_node *in_comp_grp)
{
struct cam_isp_resource_node *comp_grp = NULL;
struct cam_vfe_bus_ver2_comp_grp_data *in_rsrc_data = NULL;
int match_found = 0;
if (!in_comp_grp) {
pr_err("Invalid Params Comp Grp %pK\n", in_rsrc_data);
return -EINVAL;
}
if (in_comp_grp->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) {
/* Already Released. Do Nothing */
return 0;
}
in_rsrc_data = in_comp_grp->res_priv;
list_for_each_entry(comp_grp, &ver2_bus_priv->used_comp_grp, list) {
if (comp_grp == in_comp_grp) {
match_found = 1;
break;
}
}
if (!match_found) {
pr_err("Could not find matching Comp Grp type %u\n",
in_rsrc_data->comp_grp_type);
return -ENODEV;
}
list_del(&comp_grp->list);
if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
in_rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
list_add_tail(&comp_grp->list,
&ver2_bus_priv->free_dual_comp_grp);
else if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0
&& in_rsrc_data->comp_grp_type <=
CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
list_add_tail(&comp_grp->list, &ver2_bus_priv->free_comp_grp);
list_add_tail(&comp_grp->list,
&ver2_bus_priv->free_comp_grp);
in_rsrc_data->unique_id = 0;
in_rsrc_data->comp_grp_local_idx = 0;
in_rsrc_data->composite_mask = 0;
in_rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX;
comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
return -ENODEV;
}
static int cam_vfe_bus_start_comp_grp(struct cam_isp_resource_node *comp_grp)
{
int rc = 0;
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data =
comp_grp->res_priv;
struct cam_vfe_bus_ver2_common_data *common_data =
rsrc_data->common_data;
/*
* Individual Comp_Grp Subscribe IRQ can be done here once
* dynamic IRQ enable support is added.
*/
cam_io_w_mb(0x00001F70, common_data->mem_base + 0x2044);
cam_io_w_mb(0x000FFFE7, common_data->mem_base + 0x2048);
cam_io_w_mb(0x000000FF, common_data->mem_base + 0x204c);
cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
rsrc_data->hw_regs->comp_mask);
CDBG("composite_mask is 0x%x\n", rsrc_data->composite_mask);
CDBG("composite_mask addr 0x%x\n", rsrc_data->hw_regs->comp_mask);
if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
rsrc_data->is_master) {
int dual_comp_grp = (rsrc_data->comp_grp_type -
CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
int intra_client_en = cam_io_r_mb(common_data->mem_base +
common_data->common_reg->dual_master_comp_cfg);
/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
intra_client_en |=
(rsrc_data->intra_client_mask << dual_comp_grp * 2);
cam_io_w_mb(intra_client_en, common_data->mem_base +
common_data->common_reg->dual_master_comp_cfg);
}
comp_grp->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
return rc;
}
static int cam_vfe_bus_stop_comp_grp(struct cam_isp_resource_node *comp_grp)
{
int rc = 0;
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data =
comp_grp->res_priv;
struct cam_vfe_bus_ver2_common_data *common_data =
rsrc_data->common_data;
/* Unsubscribe IRQ */
cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
rsrc_data->hw_regs->comp_mask);
if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
rsrc_data->is_master) {
int dual_comp_grp = (rsrc_data->comp_grp_type -
CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
int intra_client_en = cam_io_r_mb(common_data->mem_base +
common_data->common_reg->dual_master_comp_cfg);
/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
intra_client_en &=
~(rsrc_data->intra_client_mask << dual_comp_grp * 2);
cam_io_w_mb(intra_client_en, common_data->mem_base +
common_data->common_reg->dual_master_comp_cfg);
}
comp_grp->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
return rc;
}
static int cam_vfe_bus_handle_comp_done_top_half(uint32_t evt_id,
struct cam_irq_th_payload *th_payload)
{
return -EPERM;
}
static int cam_vfe_bus_handle_comp_done_bottom_half(
void *handler_priv,
void *evt_payload_priv)
{
int rc = CAM_VFE_IRQ_STATUS_ERR;
struct cam_isp_resource_node *comp_grp = handler_priv;
struct cam_vfe_bus_irq_evt_payload *evt_payload = evt_payload_priv;
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data = comp_grp->res_priv;
uint32_t *cam_ife_irq_regs = evt_payload->irq_reg_val;
uint32_t status_reg;
uint32_t comp_err_reg;
uint32_t dual_comp_grp;
CDBG("comp grp type %d\n", rsrc_data->comp_grp_type);
switch (rsrc_data->comp_grp_type) {
case CAM_VFE_BUS_VER2_COMP_GRP_0:
case CAM_VFE_BUS_VER2_COMP_GRP_1:
case CAM_VFE_BUS_VER2_COMP_GRP_2:
case CAM_VFE_BUS_VER2_COMP_GRP_3:
case CAM_VFE_BUS_VER2_COMP_GRP_4:
case CAM_VFE_BUS_VER2_COMP_GRP_5:
dual_comp_grp = (rsrc_data->comp_grp_type -
CAM_VFE_BUS_VER2_COMP_GRP_0);
/* Check for Regular composite error */
status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0];
comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_COMP_ERR];
if ((status_reg & BIT(11)) &&
(comp_err_reg & rsrc_data->composite_mask)) {
/* Check for Regular composite error */
rc = CAM_VFE_IRQ_STATUS_ERR_COMP;
break;
}
comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_COMP_OWRT];
/* Check for Regular composite Overwrite */
if ((status_reg & BIT(12)) &&
(comp_err_reg & rsrc_data->composite_mask)) {
rc = CAM_VFE_IRQ_STATUS_COMP_OWRT;
break;
}
/* Regular Composite SUCCESS*/
if (status_reg & BIT(dual_comp_grp + 5))
rc = CAM_VFE_IRQ_STATUS_SUCCESS;
CDBG("status reg = 0x%x, bit index = %d\n",
status_reg, (dual_comp_grp + 5));
break;
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0:
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1:
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2:
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3:
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4:
case CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5:
dual_comp_grp = (rsrc_data->comp_grp_type -
CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
/* Check for DUAL composite error */
status_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2];
comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_DUAL_COMP_ERR];
if ((status_reg & BIT(6)) &&
(comp_err_reg & rsrc_data->composite_mask)) {
/* Check for DUAL composite error */
rc = CAM_VFE_IRQ_STATUS_ERR_COMP;
break;
}
/* Check for Dual composite Overwrite */
comp_err_reg = cam_ife_irq_regs[CAM_IFE_IRQ_BUS_DUAL_COMP_OWRT];
if ((status_reg & BIT(7)) &&
(comp_err_reg & rsrc_data->composite_mask)) {
rc = CAM_VFE_IRQ_STATUS_COMP_OWRT;
break;
}
/* DUAL Composite SUCCESS*/
if (status_reg & BIT(dual_comp_grp))
rc = CAM_VFE_IRQ_STATUS_SUCCESS;
break;
default:
rc = CAM_VFE_IRQ_STATUS_ERR;
break;
}
if (rc == CAM_VFE_IRQ_STATUS_SUCCESS)
cam_vfe_bus_put_evt_payload(evt_payload->core_info,
&evt_payload);
return rc;
}
static int cam_vfe_bus_init_comp_grp(uint32_t index,
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_vfe_bus_ver2_hw_info *ver2_hw_info,
struct cam_isp_resource_node *comp_grp)
{
struct cam_vfe_bus_ver2_comp_grp_data *rsrc_data =
comp_grp->res_priv;
rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_comp_grp_data),
GFP_KERNEL);
if (!rsrc_data) {
CDBG("Failed to alloc for comp_grp_priv\n");
return -ENOMEM;
}
comp_grp->res_priv = rsrc_data;
comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
INIT_LIST_HEAD(&comp_grp->list);
rsrc_data->comp_grp_type = index;
rsrc_data->common_data = &ver2_bus_priv->common_data;
rsrc_data->hw_regs = &ver2_hw_info->comp_grp_reg[index];
rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX;
if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
list_add_tail(&comp_grp->list,
&ver2_bus_priv->free_dual_comp_grp);
else if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0
&& rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)
list_add_tail(&comp_grp->list, &ver2_bus_priv->free_comp_grp);
comp_grp->start = cam_vfe_bus_start_comp_grp;
comp_grp->stop = cam_vfe_bus_stop_comp_grp;
comp_grp->top_half_handler = cam_vfe_bus_handle_comp_done_top_half;
comp_grp->bottom_half_handler =
cam_vfe_bus_handle_comp_done_bottom_half;
comp_grp->hw_intf = ver2_bus_priv->common_data.hw_intf;
return 0;
}
static int cam_vfe_bus_acquire_vfe_out(void *bus_priv, void *acquire_args)
{
int rc = -ENODEV;
int i;
enum cam_vfe_bus_ver2_vfe_out_type vfe_out_res_id;
uint32_t format;
uint32_t num_wm;
uint32_t subscribe_irq;
uint32_t client_done_mask;
struct cam_vfe_bus_ver2_priv *ver2_bus_priv = bus_priv;
struct cam_vfe_acquire_args *acq_args = acquire_args;
struct cam_vfe_hw_vfe_out_acquire_args *out_acquire_args;
struct cam_isp_resource_node *rsrc_node = NULL;
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL;
if (!bus_priv || !acquire_args) {
pr_err("Invalid Param");
return -EINVAL;
}
out_acquire_args = &acq_args->vfe_out;
format = out_acquire_args->out_port_info->format;
CDBG("Acquiring resource type 0x%x\n",
out_acquire_args->out_port_info->res_type);
vfe_out_res_id = cam_vfe_bus_get_out_res_id(
out_acquire_args->out_port_info->res_type);
if (vfe_out_res_id == CAM_VFE_BUS_VER2_VFE_OUT_MAX)
return -ENODEV;
num_wm = cam_vfe_bus_get_num_wm(vfe_out_res_id, format);
if (num_wm < 1)
return -EINVAL;
rsrc_node = &ver2_bus_priv->vfe_out[vfe_out_res_id];
if (rsrc_node->res_state != CAM_ISP_RESOURCE_STATE_AVAILABLE) {
pr_err("Resource not available: Res_id %d state:%d\n",
vfe_out_res_id, rsrc_node->res_state);
return -EBUSY;
}
rsrc_data = rsrc_node->res_priv;
rsrc_data->num_wm = num_wm;
rsrc_node->res_id = out_acquire_args->out_port_info->res_type;
rsrc_node->tasklet_info = acq_args->tasklet;
rsrc_node->cdm_ops = out_acquire_args->cdm_ops;
rsrc_data->cdm_util_ops = out_acquire_args->cdm_ops;
/* Reserve Composite Group */
if (num_wm > 1 || (out_acquire_args->out_port_info->comp_grp_id >
CAM_ISP_RES_COMP_GROUP_NONE &&
out_acquire_args->out_port_info->comp_grp_id <
CAM_ISP_RES_COMP_GROUP_ID_MAX)) {
rc = cam_vfe_bus_acquire_comp_grp(ver2_bus_priv,
out_acquire_args->out_port_info,
out_acquire_args->unique_id,
out_acquire_args->is_dual,
out_acquire_args->is_master,
out_acquire_args->dual_slave_core,
&rsrc_data->comp_grp);
if (rc < 0)
return rc;
subscribe_irq = 0;
} else
subscribe_irq = 1;
/* Reserve WM */
for (i = 0; i < num_wm; i++) {
rc = cam_vfe_bus_acquire_wm(ver2_bus_priv,
out_acquire_args->out_port_info,
vfe_out_res_id,
i,
out_acquire_args->split_id,
subscribe_irq,
&rsrc_data->wm_res[i],
&client_done_mask);
if (rc < 0)
goto release_wm;
if (rsrc_data->comp_grp)
cam_vfe_bus_add_wm_to_comp_grp(rsrc_data->comp_grp,
client_done_mask);
}
rsrc_node->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
out_acquire_args->rsrc_node = rsrc_node;
CDBG("Acquire successful\n");
return rc;
release_wm:
for (i--; i >= 0; i--)
cam_vfe_bus_release_wm(ver2_bus_priv, rsrc_data->wm_res[i]);
cam_vfe_bus_release_comp_grp(ver2_bus_priv,
rsrc_data->comp_grp);
return rc;
}
static int cam_vfe_bus_release_vfe_out(void *bus_priv,
struct cam_isp_resource_node *vfe_out)
{
if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
pr_err("Error! Invalid resource state:%d\n",
vfe_out->res_state);
}
if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED)
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
return 0;
}
static int cam_vfe_bus_start_vfe_out(struct cam_isp_resource_node *vfe_out)
{
int rc = 0, i;
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
CDBG("Start resource index %d\n", rsrc_data->out_type);
if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
pr_err("Error! Invalid resource state:%d\n",
vfe_out->res_state);
return -EACCES;
}
for (i = 0; i < rsrc_data->num_wm; i++)
rc = cam_vfe_bus_start_wm(rsrc_data->wm_res[i]);
if (rsrc_data->comp_grp)
rc = cam_vfe_bus_start_comp_grp(rsrc_data->comp_grp);
/* VFE_MODULE_BUS_CGC_OVERRIDE */
cam_io_w_mb(0xFFFFFFFF, rsrc_data->common_data->mem_base + 0x0000003C);
/* VFE_MODULE_COLOR_CGC_OVERRIDE */
cam_io_w_mb(0xFFFFFFFF, rsrc_data->common_data->mem_base + 0x00000034);
/* VFE_MODULE_ZOOM_CGC_OVERRIDE */
cam_io_w_mb(0xFFFFFFFF, rsrc_data->common_data->mem_base + 0x38);
/* VFE_MODULE_LENS_CGC_OVERRIDE */
cam_io_w_mb(0xFFFFFFFF, rsrc_data->common_data->mem_base + 0x0000002C);
/* VFE_MODULE_STATS_CGC_OVERRIDE */
cam_io_w_mb(0xFFFFFFFF, rsrc_data->common_data->mem_base + 0x00000030);
/* BUS_WR_INPUT_IF_ADDR_SYNC_CFG */
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000207C);
/* BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002080);
/* BUS_WR_INPUT_IF_ADDR_SYNC_NO_SYNC */
cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x00002084);
/* no clock gating at bus input */
cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x0000200C);
/* BUS_WR_PWR_ISO_CFG */
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020CC);
/* BUS_WR_TEST_BUS_CTRL */
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000211C);
/* BUS_WR_INPUT_IF_ADDR_SYNC_0 */
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002088);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000208c);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002090);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002094);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002098);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000209c);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a0);
cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a4);
return rc;
}
static int cam_vfe_bus_stop_vfe_out(struct cam_isp_resource_node *vfe_out)
{
int rc = 0, i;
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE ||
vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) {
return rc;
}
if (rsrc_data->comp_grp)
rc = cam_vfe_bus_stop_comp_grp(rsrc_data->comp_grp);
for (i = 0; i < rsrc_data->num_wm; i++)
rc = cam_vfe_bus_stop_wm(rsrc_data->wm_res[i]);
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
return rc;
}
static int cam_vfe_bus_handle_vfe_out_done_top_half(uint32_t evt_id,
struct cam_irq_th_payload *th_payload)
{
return -EPERM;
}
static int cam_vfe_bus_handle_vfe_out_done_bottom_half(
void *handler_priv,
void *evt_payload_priv)
{
int rc = -EINVAL;
struct cam_isp_resource_node *vfe_out = handler_priv;
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
/*
* If this resource has Composite Group then we only handle
* Composite done. We acquire Composite if number of WM > 1.
* So Else case is only one individual buf_done = WM[0].
*/
if (rsrc_data->comp_grp) {
rc = rsrc_data->comp_grp->bottom_half_handler(
rsrc_data->comp_grp, evt_payload_priv);
} else {
rc = rsrc_data->wm_res[0]->bottom_half_handler(
rsrc_data->comp_grp, evt_payload_priv);
}
return rc;
}
static int cam_vfe_bus_init_vfe_out_resource(uint32_t index,
struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
struct cam_vfe_bus_ver2_hw_info *ver2_hw_info,
struct cam_isp_resource_node *vfe_out)
{
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL;
int rc = 0;
rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_vfe_out_data),
GFP_KERNEL);
if (!rsrc_data) {
CDBG("Error! Failed to alloc for vfe out priv\n");
rc = -ENOMEM;
return rc;
}
vfe_out->res_priv = rsrc_data;
vfe_out->res_type = CAM_ISP_RESOURCE_VFE_OUT;
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
INIT_LIST_HEAD(&vfe_out->list);
rsrc_data->out_type = index;
rsrc_data->common_data = &ver2_bus_priv->common_data;
rsrc_data->max_width =
ver2_hw_info->vfe_out_hw_info[index].max_width;
rsrc_data->max_height =
ver2_hw_info->vfe_out_hw_info[index].max_height;
vfe_out->start = cam_vfe_bus_start_vfe_out;
vfe_out->stop = cam_vfe_bus_stop_vfe_out;
vfe_out->top_half_handler = cam_vfe_bus_handle_vfe_out_done_top_half;
vfe_out->bottom_half_handler =
cam_vfe_bus_handle_vfe_out_done_bottom_half;
vfe_out->hw_intf = ver2_bus_priv->common_data.hw_intf;
return 0;
}
static int cam_vfe_bus_get_evt_payload(
struct cam_vfe_bus_ver2_priv *bus_priv,
struct cam_vfe_bus_irq_evt_payload **evt_payload)
{
if (list_empty(&bus_priv->free_payload_list)) {
pr_err("No free payload\n");
return -ENODEV;
}
*evt_payload = list_first_entry(&bus_priv->free_payload_list,
struct cam_vfe_bus_irq_evt_payload, list);
list_del_init(&(*evt_payload)->list);
return 0;
}
static int cam_vfe_bus_put_evt_payload(void *core_info,
struct cam_vfe_bus_irq_evt_payload **evt_payload)
{
struct cam_vfe_bus_ver2_priv *bus_priv = NULL;
if (!core_info) {
pr_err("Invalid param core_info NULL");
return -EINVAL;
}
if (*evt_payload == NULL) {
pr_err("No payload to put\n");
return -EINVAL;
}
bus_priv = (*evt_payload)->bus_priv;
list_add_tail(&(*evt_payload)->list, &bus_priv->free_payload_list);
*evt_payload = NULL;
return 0;
}
static int cam_vfe_bus_ver2_handle_irq(uint32_t evt_id,
struct cam_irq_th_payload *th_payload)
{
int32_t rc;
int i;
struct cam_vfe_irq_handler_priv *handler_priv;
struct cam_vfe_hw_core_info *core_info;
struct cam_vfe_bus_irq_evt_payload *evt_payload;
struct cam_vfe_bus *bus_info;
struct cam_vfe_bus_ver2_priv *bus_priv;
struct cam_irq_controller_reg_info *reg_info;
handler_priv = th_payload->handler_priv;
core_info = handler_priv->core_info;
bus_info = core_info->vfe_bus;
bus_priv = bus_info->bus_priv;
reg_info = &bus_priv->common_data.common_reg->irq_reg_info;
/*
* add reset ack handling here once supported.
* Just clear all the bus irq status registers and ignore the reset.
*/
CDBG("Enter\n");
rc = cam_vfe_bus_get_evt_payload(bus_priv, &evt_payload);
if (rc) {
pr_err("No tasklet_cmd is free in queue\n");
return rc;
}
cam_isp_hw_get_timestamp(&evt_payload->ts);
evt_payload->core_index = handler_priv->core_index;
evt_payload->core_info = handler_priv->core_info;
evt_payload->bus_priv = bus_priv;
CDBG("core_idx %d, core_info %llx\n", handler_priv->core_index,
(uint64_t)handler_priv->core_info);
for (i = 0; i < CAM_IFE_BUS_IRQ_REGISTERS_MAX; i++) {
evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base +
irq_reg_offset[i]);
CDBG("irq_status%d = 0x%x\n", i, evt_payload->irq_reg_val[i]);
}
for (i = 0; i <= CAM_IFE_IRQ_BUS_REG_STATUS2; i++) {
cam_io_w(evt_payload->irq_reg_val[i], handler_priv->mem_base +
reg_info->irq_reg_set[i].clear_reg_offset);
CDBG("Clear irq_status%d = 0x%x offset 0x%x\n", i,
evt_payload->irq_reg_val[i],
reg_info->irq_reg_set[i].clear_reg_offset);
}
cam_io_w(reg_info->global_clear_bitmask, handler_priv->mem_base +
reg_info->global_clear_offset);
CDBG("Global clear bitmask = 0x%x offset 0x%x\n",
reg_info->global_clear_bitmask,
reg_info->global_clear_offset);
th_payload->evt_payload_priv = evt_payload;
return rc;
}
static int cam_vfe_bus_update_buf(void *priv, void *cmd_args,
uint32_t arg_size)
{
struct cam_vfe_bus_ver2_priv *bus_priv;
struct cam_isp_hw_get_buf_update *update_buf;
struct cam_vfe_bus_ver2_vfe_out_data *vfe_out_data = NULL;
struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL;
uint32_t reg_val_pair[6];
uint32_t i, size = 0;
/*
* Need the entire buf io config so we can get the stride info
* for the wm.
*/
bus_priv = (struct cam_vfe_bus_ver2_priv *) priv;
update_buf = (struct cam_isp_hw_get_buf_update *) cmd_args;
vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *)
update_buf->cdm.res->res_priv;
if (!vfe_out_data || !vfe_out_data->cdm_util_ops) {
pr_err("Failed! Invalid data\n");
return -EINVAL;
}
if (update_buf->num_buf < vfe_out_data->num_wm) {
pr_err("Failed! Invalid number buffers:%d required:%d\n",
update_buf->num_buf, vfe_out_data->num_wm);
return -ENOMEM;
}
size = vfe_out_data->cdm_util_ops->cdm_required_size_reg_random(
vfe_out_data->num_wm);
/* cdm util returns dwords, need to convert to bytes */
if ((size * 4) > update_buf->cdm.size) {
pr_err("Failed! Buf size:%d insufficient, expected size:%d\n",
update_buf->cdm.size, size);
return -ENOMEM;
}
for (i = 0 ; i < vfe_out_data->num_wm; i++) {
wm_data = vfe_out_data->wm_res[i]->res_priv;
reg_val_pair[2 * i] = wm_data->hw_regs->image_addr;
reg_val_pair[2 * i + 1] = update_buf->image_buf[i];
CDBG("offset 0x%x, value 0x%llx\n",
wm_data->hw_regs->image_addr,
(uint64_t) update_buf->image_buf[i]);
}
vfe_out_data->cdm_util_ops->cdm_write_regrandom(
update_buf->cdm.cmd_buf_addr,
vfe_out_data->num_wm, reg_val_pair);
/* cdm util returns dwords, need to convert to bytes */
update_buf->cdm.used_bytes = size * 4;
return 0;
}
static int cam_vfe_bus_process_cmd(void *priv,
uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
{
int rc = -EINVAL;
if (!priv || !cmd_args) {
pr_err_ratelimited("Error! Invalid input arguments\n");
return -EINVAL;
}
switch (cmd_type) {
case CAM_VFE_HW_CMD_GET_BUF_UPDATE:
rc = cam_vfe_bus_update_buf(priv, cmd_args, arg_size);
break;
default:
pr_err_ratelimited("Error! Invalid camif process command:%d\n",
cmd_type);
break;
}
return rc;
}
int cam_vfe_bus_ver2_init(
void __iomem *mem_base,
struct cam_hw_intf *hw_intf,
void *bus_hw_info,
void *vfe_irq_controller,
struct cam_vfe_bus **vfe_bus)
{
int i, rc = 0;
struct cam_vfe_bus_ver2_priv *bus_priv = NULL;
struct cam_vfe_bus *vfe_bus_local;
struct cam_vfe_bus_ver2_hw_info *ver2_hw_info = bus_hw_info;
CDBG("Enter\n");
vfe_bus_local = kzalloc(sizeof(struct cam_vfe_bus), GFP_KERNEL);
if (!vfe_bus_local) {
CDBG("Failed to alloc for vfe_bus\n");
rc = -ENOMEM;
goto err_alloc_bus;
}
bus_priv = kzalloc(sizeof(struct cam_vfe_bus_ver2_priv),
GFP_KERNEL);
if (!bus_priv) {
CDBG("Failed to alloc for vfe_bus_priv\n");
rc = -ENOMEM;
goto err_alloc_priv;
}
vfe_bus_local->bus_priv = bus_priv;
bus_priv->common_data.mem_base = mem_base;
bus_priv->common_data.hw_intf = hw_intf;
bus_priv->common_data.vfe_irq_controller = vfe_irq_controller;
bus_priv->common_data.common_reg = &ver2_hw_info->common_reg;
INIT_LIST_HEAD(&bus_priv->free_comp_grp);
INIT_LIST_HEAD(&bus_priv->free_dual_comp_grp);
INIT_LIST_HEAD(&bus_priv->used_comp_grp);
for (i = 0; i < CAM_VFE_BUS_VER2_MAX_CLIENTS; i++) {
rc = cam_vfe_bus_init_wm_resource(i, bus_priv, bus_hw_info,
&bus_priv->bus_client[i]);
if (rc < 0) {
pr_err("Error! Init WM failed\n");
goto err_init_wm;
}
}
for (i = 0; i < CAM_VFE_BUS_VER2_COMP_GRP_MAX; i++) {
rc = cam_vfe_bus_init_comp_grp(i, bus_priv, bus_hw_info,
&bus_priv->comp_grp[i]);
if (rc < 0) {
pr_err("Error! Init Comp Grp failed\n");
goto err_init_comp_grp;
}
}
for (i = 0; i < CAM_VFE_BUS_VER2_VFE_OUT_MAX; i++) {
rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv, bus_hw_info,
&bus_priv->vfe_out[i]);
if (rc < 0) {
pr_err("Error! Init VFE Out failed\n");
goto err_init_vfe_out;
}
}
INIT_LIST_HEAD(&bus_priv->free_payload_list);
for (i = 0; i < 128; i++) {
INIT_LIST_HEAD(&bus_priv->evt_payload[i].list);
list_add_tail(&bus_priv->evt_payload[i].list,
&bus_priv->free_payload_list);
}
vfe_bus_local->acquire_resource = cam_vfe_bus_acquire_vfe_out;
vfe_bus_local->release_resource = cam_vfe_bus_release_vfe_out;
vfe_bus_local->start_resource = cam_vfe_bus_start_vfe_out;
vfe_bus_local->stop_resource = cam_vfe_bus_stop_vfe_out;
vfe_bus_local->top_half_handler = cam_vfe_bus_ver2_handle_irq;
vfe_bus_local->bottom_half_handler = NULL;
vfe_bus_local->process_cmd = cam_vfe_bus_process_cmd;
*vfe_bus = vfe_bus_local;
return rc;
err_init_vfe_out:
err_init_comp_grp:
err_init_wm:
kfree(vfe_bus_local->bus_priv);
err_alloc_priv:
kfree(vfe_bus_local);
err_alloc_bus:
return rc;
}