Merge "msm: camera: isp: Add VFE HW driver support" into msm-4.9
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt
new file mode 100644
index 0000000..1c18228
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt
@@ -0,0 +1,109 @@
+* Qualcomm Technologies, Inc. MSM Camera VFE
+
+Camera VFE device provides the definitions for enabling
+the VFE hardware. It also provides the functions for the client
+to control the VFE hardware.
+
+=======================
+Required Node Structure
+=======================
+The VFE device is described in one level of the device node.
+
+======================================
+First Level Node - CAM VFE device
+======================================
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should specify the compatibility string for matching the
+ driver. e.g. "qcom,vfe170", "qcom,vfe-lite170".
+
+- cell-index
+ Usage: required
+ Value type: <u32>
+ Definition: Should specify the hardware index id.
+
+- reg-names
+ Usage: required
+ Value type: <string>
+ Definition: Should specify the name of the register block.
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: Register values.
+
+- interrupt-names
+ Usage: Required
+ Value type: <string>
+ Definition: Name of the interrupt.
+
+- interrupts
+ Usage: Required
+ Value type: <u32>
+ Definition: Interrupt associated with VFE HW.
+
+- regulator-names
+ Usage: required
+ Value type: <string>
+ Definition: Name of the regulator resources for VFE HW.
+
+- xxxx-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: Regulator reference corresponding to the names listed in
+ "regulator-names".
+
+- clock-names
+ Usage: required
+ Value type: <string>
+ Definition: List of clock names required for VFE HW.
+
+- clocks
+ Usage: required
+ Value type: <phandle>
+ Definition: List of clocks used for VFE HW.
+
+- clock-rates
+ Usage: required
+ Value type: <u32>
+ Definition: List of clocks rates.
+
+- src-clock-name
+ Usage: required
+ Value type: <string>
+ Definition: Source clock name.
+
+Example:
+ qcom,vfe0@acaf000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe170";
+ reg-names = "ife";
+ reg = <0xacaf000 0x4000>;
+ interrupts = <0 465 0>;
+ interrupt-names = "ife";
+ vdd-names = "camss-vdd", "ife0-vdd";
+ camss-vdd-supply = <&titan_top_gdsc>;
+ ife0-vdd-supply = <&ife_0_gdsc>;
+ clock-names = "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "slow_ahb_clk_src",
+ "ife_clk",
+ "ife_clk_src",
+ "ife_csid_clk",
+ "ife_csid_clk_src",
+ "camnoc_axi_clk",
+ "ife_axi_clk",
+ clocks = <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK_SRC>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_AXI_CLK>,
+ clock-rates = <0 0 80000000 0 320000000 0 384000000 0 0 0>;
+ src-clock-name = "ife_clk_src";
+ status = "ok";
+ };
diff --git a/drivers/media/platform/msm/camera/cam_isp/Makefile b/drivers/media/platform/msm/camera/cam_isp/Makefile
index b6e2d09..77ad6fc 100644
--- a/drivers/media/platform/msm/camera/cam_isp/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/Makefile
@@ -4,4 +4,5 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
+obj-$(CONFIG_SPECTRA_CAMERA) += isp_hw_mgr/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp_dev.o cam_isp_context.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile
new file mode 100644
index 0000000..68a1a7d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SPECTRA_CAMERA) += hw_utils/ isp_hw/
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/Makefile
new file mode 100644
index 0000000..076c2cf
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/Makefile
@@ -0,0 +1,11 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_tasklet_util.o
+obj-$(CONFIG_SPECTRA_CAMERA) += irq_controller/
\ No newline at end of file
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
new file mode 100644
index 0000000..ecc71b3
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
@@ -0,0 +1,322 @@
+/* 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/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/ratelimit.h>
+#include "cam_tasklet_util.h"
+#include "cam_irq_controller.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define CAM_TASKLETQ_SIZE 256
+
+static void cam_tasklet_action(unsigned long data);
+
+/**
+ * struct cam_tasklet_queue_cmd:
+ * @Brief: Structure associated with each slot in the
+ * tasklet queue
+ *
+ * @list: list_head member for each entry in queue
+ * @payload: Payload structure for the event. This will be
+ * passed to the handler function
+ * @bottom_half_handler: Function pointer for event handler in bottom
+ * half context
+ *
+ */
+struct cam_tasklet_queue_cmd {
+ struct list_head list;
+ void *payload;
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler;
+};
+
+/**
+ * struct cam_tasklet_info:
+ * @Brief: Tasklet private structure
+ *
+ * @list: list_head member for each tasklet
+ * @index: Instance id for the tasklet
+ * @tasklet_lock: Spin lock
+ * @tasklet_active: Atomic variable to control tasklet state
+ * @tasklet: Tasklet structure used to schedule bottom half
+ * @free_cmd_list: List of free tasklet queue cmd for use
+ * @used_cmd_list: List of used tasklet queue cmd
+ * @cmd_queue: Array of tasklet cmd for storage
+ * @ctx_priv: Private data passed to the handling function
+ *
+ */
+struct cam_tasklet_info {
+ struct list_head list;
+ uint32_t index;
+ spinlock_t tasklet_lock;
+ atomic_t tasklet_active;
+ struct tasklet_struct tasklet;
+
+ struct list_head free_cmd_list;
+ struct list_head used_cmd_list;
+ struct cam_tasklet_queue_cmd cmd_queue[CAM_TASKLETQ_SIZE];
+
+ void *ctx_priv;
+};
+
+/**
+ * cam_tasklet_get_cmd()
+ *
+ * @brief: Get free cmd from tasklet
+ *
+ * @tasklet: Tasklet Info structure to get cmd from
+ * @tasklet_cmd: Return tasklet_cmd pointer if successful
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+static int cam_tasklet_get_cmd(
+ struct cam_tasklet_info *tasklet,
+ struct cam_tasklet_queue_cmd **tasklet_cmd)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ *tasklet_cmd = NULL;
+
+ if (!atomic_read(&tasklet->tasklet_active)) {
+ pr_err_ratelimited("Tasklet is not active!\n");
+ rc = -EPIPE;
+ return rc;
+ }
+
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ if (list_empty(&tasklet->free_cmd_list)) {
+ pr_err_ratelimited("No more free tasklet cmd!\n");
+ rc = -ENODEV;
+ goto spin_unlock;
+ } else {
+ *tasklet_cmd = list_first_entry(&tasklet->free_cmd_list,
+ struct cam_tasklet_queue_cmd, list);
+ list_del_init(&(*tasklet_cmd)->list);
+ }
+
+spin_unlock:
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ return rc;
+}
+
+/**
+ * cam_tasklet_put_cmd()
+ *
+ * @brief: Put back cmd to free list
+ *
+ * @tasklet: Tasklet Info structure to put cmd into
+ * @tasklet_cmd: tasklet_cmd pointer that needs to be put back
+ *
+ * @return: Void
+ */
+static void cam_tasklet_put_cmd(
+ struct cam_tasklet_info *tasklet,
+ struct cam_tasklet_queue_cmd **tasklet_cmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ list_add_tail(&(*tasklet_cmd)->list,
+ &tasklet->free_cmd_list);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+}
+
+/**
+ * cam_tasklet_dequeue_cmd()
+ *
+ * @brief: Initialize the tasklet info structure
+ *
+ * @hw_mgr_ctx: Private Ctx data that will be passed to the handler
+ * function
+ * @idx: Index of tasklet used as identity
+ * @tasklet_action: Tasklet callback function that will be called
+ * when tasklet runs on CPU
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+static int cam_tasklet_dequeue_cmd(
+ struct cam_tasklet_info *tasklet,
+ struct cam_tasklet_queue_cmd **tasklet_cmd)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ *tasklet_cmd = NULL;
+
+ if (!atomic_read(&tasklet->tasklet_active)) {
+ pr_err("Tasklet is not active!\n");
+ rc = -EPIPE;
+ return rc;
+ }
+
+ CDBG("Dequeue before lock.\n");
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ if (list_empty(&tasklet->used_cmd_list)) {
+ CDBG("End of list reached. Exit\n");
+ rc = -ENODEV;
+ goto spin_unlock;
+ } else {
+ *tasklet_cmd = list_first_entry(&tasklet->used_cmd_list,
+ struct cam_tasklet_queue_cmd, list);
+ list_del_init(&(*tasklet_cmd)->list);
+ CDBG("Dequeue Successful\n");
+ }
+
+spin_unlock:
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ return rc;
+}
+
+int cam_tasklet_enqueue_cmd(
+ void *bottom_half,
+ void *handler_priv,
+ void *evt_payload_priv,
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler)
+{
+ struct cam_tasklet_info *tasklet = bottom_half;
+ struct cam_tasklet_queue_cmd *tasklet_cmd = NULL;
+ unsigned long flags;
+ int rc;
+
+ if (!bottom_half) {
+ pr_err("NULL bottom half\n");
+ return -EINVAL;
+ }
+
+ rc = cam_tasklet_get_cmd(tasklet, &tasklet_cmd);
+
+ if (tasklet_cmd) {
+ CDBG("%s: Enqueue tasklet cmd\n", __func__);
+ tasklet_cmd->bottom_half_handler = bottom_half_handler;
+ tasklet_cmd->payload = evt_payload_priv;
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ list_add_tail(&tasklet_cmd->list,
+ &tasklet->used_cmd_list);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ tasklet_schedule(&tasklet->tasklet);
+ } else {
+ pr_err("%s: tasklet cmd is NULL!\n", __func__);
+ }
+
+ return rc;
+}
+
+int cam_tasklet_init(
+ void **tasklet_info,
+ void *hw_mgr_ctx,
+ uint32_t idx)
+{
+ int i;
+ struct cam_tasklet_info *tasklet = NULL;
+
+ tasklet = kzalloc(sizeof(struct cam_tasklet_info), GFP_KERNEL);
+ if (!tasklet) {
+ CDBG("Error! Unable to allocate memory for tasklet");
+ *tasklet_info = NULL;
+ return -ENOMEM;
+ }
+
+ tasklet->ctx_priv = hw_mgr_ctx;
+ tasklet->index = idx;
+ spin_lock_init(&tasklet->tasklet_lock);
+ memset(tasklet->cmd_queue, 0, sizeof(tasklet->cmd_queue));
+ INIT_LIST_HEAD(&tasklet->free_cmd_list);
+ INIT_LIST_HEAD(&tasklet->used_cmd_list);
+ for (i = 0; i < CAM_TASKLETQ_SIZE; i++) {
+ INIT_LIST_HEAD(&tasklet->cmd_queue[i].list);
+ list_add_tail(&tasklet->cmd_queue[i].list,
+ &tasklet->free_cmd_list);
+ }
+ tasklet_init(&tasklet->tasklet, cam_tasklet_action,
+ (unsigned long)tasklet);
+ cam_tasklet_stop(tasklet);
+
+ *tasklet_info = tasklet;
+
+ return 0;
+}
+
+void cam_tasklet_deinit(void **tasklet_info)
+{
+ struct cam_tasklet_info *tasklet = *tasklet_info;
+
+ atomic_set(&tasklet->tasklet_active, 0);
+ tasklet_kill(&tasklet->tasklet);
+ kfree(tasklet);
+ *tasklet_info = NULL;
+}
+
+int cam_tasklet_start(void *tasklet_info)
+{
+ struct cam_tasklet_info *tasklet = tasklet_info;
+ struct cam_tasklet_queue_cmd *tasklet_cmd;
+ struct cam_tasklet_queue_cmd *tasklet_cmd_temp;
+
+ if (atomic_read(&tasklet->tasklet_active)) {
+ pr_err("Tasklet already active. idx = %d\n", tasklet->index);
+ return -EBUSY;
+ }
+ atomic_set(&tasklet->tasklet_active, 1);
+
+ /* flush the command queue first */
+ list_for_each_entry_safe(tasklet_cmd, tasklet_cmd_temp,
+ &tasklet->used_cmd_list, list) {
+ list_del_init(&tasklet_cmd->list);
+ list_add_tail(&tasklet_cmd->list, &tasklet->free_cmd_list);
+ }
+
+ tasklet_enable(&tasklet->tasklet);
+
+ return 0;
+}
+
+void cam_tasklet_stop(void *tasklet_info)
+{
+ struct cam_tasklet_info *tasklet = tasklet_info;
+
+ atomic_set(&tasklet->tasklet_active, 0);
+ tasklet_disable(&tasklet->tasklet);
+}
+
+/*
+ * cam_tasklet_action()
+ *
+ * @brief: Process function that will be called when tasklet runs
+ * on CPU
+ *
+ * @data: Tasklet Info structure that is passed in tasklet_init
+ *
+ * @return: Void
+ */
+static void cam_tasklet_action(unsigned long data)
+{
+ struct cam_tasklet_info *tasklet_info = NULL;
+ struct cam_tasklet_queue_cmd *tasklet_cmd = NULL;
+
+ tasklet_info = (struct cam_tasklet_info *)data;
+
+ while (!cam_tasklet_dequeue_cmd(tasklet_info, &tasklet_cmd)) {
+ tasklet_cmd->bottom_half_handler(tasklet_info->ctx_priv,
+ tasklet_cmd->payload);
+ cam_tasklet_put_cmd(tasklet_info, &tasklet_cmd);
+ }
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h
new file mode 100644
index 0000000..0e4bf12
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_tasklet_util.h
@@ -0,0 +1,95 @@
+/* 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.
+ */
+
+#ifndef _CAM_TASKLET_UTIL_H_
+#define _CAM_TASKLET_UTIL_H_
+
+#include "cam_irq_controller.h"
+
+/*
+ * cam_tasklet_init()
+ *
+ * @brief: Initialize the tasklet info structure
+ *
+ * @tasklet: Tasklet to initialize
+ * @hw_mgr_ctx: Private Ctx data that will be passed to the handler
+ * function
+ * @idx: Index of tasklet used as identity
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_tasklet_init(
+ void **tasklet,
+ void *hw_mgr_ctx,
+ uint32_t idx);
+
+/*
+ * cam_tasklet_deinit()
+ *
+ * @brief: Deinitialize the tasklet info structure
+ *
+ * @tasklet: Tasklet to deinitialize
+ *
+ * @return: Void
+ */
+void cam_tasklet_deinit(void **tasklet);
+
+/*
+ * cam_tasklet_start()
+ *
+ * @brief: Enable the tasklet to be scheduled and run.
+ * Caller should make sure this function is called
+ * before trying to enqueue.
+ *
+ * @tasklet: Tasklet to start
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_tasklet_start(void *tasklet);
+
+/*
+ * cam_tasklet_stop()
+ *
+ * @brief: Disable the tasklet so it can no longer be scheduled.
+ * Need to enable again to run.
+ *
+ * @tasklet: Tasklet to stop
+ *
+ * @return: Void
+ */
+void cam_tasklet_stop(void *tasklet);
+
+/*
+ * cam_tasklet_enqueue_cmd()
+ *
+ * @brief: Enqueue the tasklet_cmd to used list
+ *
+ * @bottom_half: Tasklet info to enqueue onto
+ * @handler_priv: Private Handler data that will be passed to the
+ * handler function
+ * @evt_payload_priv: Event payload that will be passed to the handler
+ * function
+ * @bottom_half_handler: Callback function that will be called by tasklet
+ * for handling event
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_tasklet_enqueue_cmd(
+ void *bottom_half,
+ void *handler_priv,
+ void *evt_payload_priv,
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler);
+
+#endif /* _CAM_TASKLET_UTIL_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile
new file mode 100644
index 0000000..1fc0dd2
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_irq_controller.o
\ No newline at end of file
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
new file mode 100644
index 0000000..366ac8a
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
@@ -0,0 +1,513 @@
+/* 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/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include "cam_io_util.h"
+#include "cam_irq_controller.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+/**
+ * struct cam_irq_evt_handler:
+ * @Brief: Event handler information
+ *
+ * @priority: Priority level of this event
+ * @evt_bit_mask_arr: evt_bit_mask that has the bits set for IRQs to
+ * subscribe for
+ * @handler_priv: Private data that will be passed to the Top/Bottom
+ * Half handler function
+ * @top_half_handler: Top half Handler callback function
+ * @bottom_half_handler: Bottom half Handler callback function
+ * @bottom_half: Pointer to bottom_half implementation on which to
+ * enqueue the event for further handling
+ * @bottom_half_enqueue_func:
+ * Function used to enqueue the bottom_half event
+ * @list_node: list_head struct used for overall handler List
+ * @th_list_node: list_head struct used for top half handler List
+ */
+struct cam_irq_evt_handler {
+ enum cam_irq_priority_level priority;
+ uint32_t *evt_bit_mask_arr;
+ void *handler_priv;
+ CAM_IRQ_HANDLER_TOP_HALF top_half_handler;
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler;
+ void *bottom_half;
+ CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC bottom_half_enqueue_func;
+ struct list_head list_node;
+ struct list_head th_list_node;
+ int index;
+};
+
+/**
+ * struct cam_irq_register_obj:
+ * @Brief: Structure containing information related to
+ * a particular register Set
+ *
+ * @index: Index of set in Array
+ * @mask_reg_offset: Offset of IRQ MASK register
+ * @clear_reg_offset: Offset of IRQ CLEAR register
+ * @status_reg_offset: Offset of IRQ STATUS register
+ * @top_half_enable_mask: Array of enabled bit_mask sorted by priority
+ */
+struct cam_irq_register_obj {
+ uint32_t index;
+ uint32_t mask_reg_offset;
+ uint32_t clear_reg_offset;
+ uint32_t status_reg_offset;
+ uint32_t top_half_enable_mask[CAM_IRQ_PRIORITY_MAX];
+};
+
+/**
+ * struct cam_irq_controller:
+ *
+ * @brief: IRQ Controller structure.
+ *
+ * @name: Name of IRQ Controller block
+ * @mem_base: Mapped base address of register space to which
+ * register offsets are added to access registers
+ * @num_registers: Number of sets(mask/clear/status) of IRQ registers
+ * @irq_register_arr: Array of Register object associated with this
+ * Controller
+ * @irq_status_arr: Array of IRQ Status values
+ * @global_clear_offset: Offset of Global IRQ Clear register. This register
+ * contains the BIT that needs to be set for the CLEAR
+ * to take effect
+ * @global_clear_bitmask: Bitmask needed to be used in Global Clear register
+ * for Clear IRQ cmd to take effect
+ * @evt_handler_list_head: List of all event handlers
+ * @th_list_head: List of handlers sorted by priority
+ * @hdl_idx: Unique identity of handler assigned on Subscribe.
+ * Used to Unsubscribe.
+ * @rw_lock: Read-Write Lock for use by controller
+ */
+struct cam_irq_controller {
+ const char *name;
+ void __iomem *mem_base;
+ uint32_t num_registers;
+ struct cam_irq_register_obj *irq_register_arr;
+ uint32_t *irq_status_arr;
+ uint32_t global_clear_offset;
+ uint32_t global_clear_bitmask;
+ struct list_head evt_handler_list_head;
+ struct list_head th_list_head[CAM_IRQ_PRIORITY_MAX];
+ uint32_t hdl_idx;
+ rwlock_t rw_lock;
+};
+
+int cam_irq_controller_deinit(void **irq_controller)
+{
+ struct cam_irq_controller *controller = *irq_controller;
+ struct cam_irq_evt_handler *evt_handler = NULL;
+
+ while (!list_empty(&controller->evt_handler_list_head)) {
+ evt_handler = list_first_entry(
+ &controller->evt_handler_list_head,
+ struct cam_irq_evt_handler, list_node);
+ list_del_init(&evt_handler->list_node);
+ kfree(evt_handler);
+ }
+
+ kfree(controller->irq_register_arr);
+ kfree(controller->irq_status_arr);
+ kfree(controller);
+ *irq_controller = NULL;
+ return 0;
+}
+
+int cam_irq_controller_init(const char *name,
+ void __iomem *mem_base,
+ struct cam_irq_controller_reg_info *register_info,
+ void **irq_controller)
+{
+ struct cam_irq_controller *controller = NULL;
+ int i, rc = 0;
+
+ *irq_controller = NULL;
+
+ if (!register_info->num_registers || !register_info->irq_reg_set ||
+ !name || !mem_base) {
+ pr_err("Invalid parameters\n");
+ rc = -EINVAL;
+ return rc;
+ }
+
+ controller = kzalloc(sizeof(struct cam_irq_controller), GFP_KERNEL);
+ if (!controller) {
+ CDBG("Failed to allocate IRQ Controller\n");
+ return -ENOMEM;
+ }
+
+ controller->irq_register_arr = kzalloc(register_info->num_registers *
+ sizeof(struct cam_irq_register_obj), GFP_KERNEL);
+ if (!controller->irq_register_arr) {
+ CDBG("Failed to allocate IRQ register Arr\n");
+ rc = -ENOMEM;
+ goto reg_alloc_error;
+ }
+
+ controller->irq_status_arr = kzalloc(register_info->num_registers *
+ sizeof(uint32_t), GFP_KERNEL);
+ if (!controller->irq_status_arr) {
+ CDBG("Failed to allocate IRQ status Arr\n");
+ rc = -ENOMEM;
+ goto status_alloc_error;
+ }
+ controller->name = name;
+
+ CDBG("num_registers: %d\n", register_info->num_registers);
+ for (i = 0; i < register_info->num_registers; i++) {
+ controller->irq_register_arr[i].index = i;
+ controller->irq_register_arr[i].mask_reg_offset =
+ register_info->irq_reg_set[i].mask_reg_offset;
+ controller->irq_register_arr[i].clear_reg_offset =
+ register_info->irq_reg_set[i].clear_reg_offset;
+ controller->irq_register_arr[i].status_reg_offset =
+ register_info->irq_reg_set[i].status_reg_offset;
+ CDBG("i %d mask_reg_offset: 0x%x\n", i,
+ controller->irq_register_arr[i].mask_reg_offset);
+ CDBG("i %d clear_reg_offset: 0x%x\n", i,
+ controller->irq_register_arr[i].clear_reg_offset);
+ CDBG("i %d status_reg_offset: 0x%x\n", i,
+ controller->irq_register_arr[i].status_reg_offset);
+ }
+ controller->num_registers = register_info->num_registers;
+ controller->global_clear_bitmask = register_info->global_clear_bitmask;
+ controller->global_clear_offset = register_info->global_clear_offset;
+ controller->mem_base = mem_base;
+
+ CDBG("global_clear_bitmask: 0x%x\n",
+ controller->global_clear_bitmask);
+ CDBG("global_clear_offset: 0x%x\n",
+ controller->global_clear_offset);
+ CDBG("mem_base: 0x%llx\n", (uint64_t)controller->mem_base);
+
+ INIT_LIST_HEAD(&controller->evt_handler_list_head);
+ for (i = 0; i < CAM_IRQ_PRIORITY_MAX; i++)
+ INIT_LIST_HEAD(&controller->th_list_head[i]);
+
+ rwlock_init(&controller->rw_lock);
+
+ controller->hdl_idx = 1;
+ *irq_controller = controller;
+
+ return rc;
+
+status_alloc_error:
+ kfree(controller->irq_register_arr);
+reg_alloc_error:
+ kfree(controller);
+
+ return rc;
+}
+
+int cam_irq_controller_subscribe_irq(void *irq_controller,
+ enum cam_irq_priority_level priority,
+ uint32_t *evt_bit_mask_arr,
+ void *handler_priv,
+ CAM_IRQ_HANDLER_TOP_HALF top_half_handler,
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler,
+ void *bottom_half,
+ CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC bottom_half_enqueue_func)
+{
+ struct cam_irq_controller *controller = irq_controller;
+ struct cam_irq_evt_handler *evt_handler = NULL;
+ int i;
+ int rc = 0;
+ uint32_t irq_mask;
+ unsigned long flags;
+
+ if (!controller || !handler_priv || !evt_bit_mask_arr) {
+ pr_err("Invalid params: ctlr=%pK handler_priv=%pK bit_mask_arr = %pK\n",
+ controller, handler_priv, evt_bit_mask_arr);
+ return -EINVAL;
+ }
+
+ if (!top_half_handler) {
+ pr_err("Missing top half handler\n");
+ return -EINVAL;
+ }
+
+ if (bottom_half_handler &&
+ (!bottom_half || !bottom_half_enqueue_func)) {
+ pr_err("Invalid params: bh_handler=%pK bh=%pK bh_enq_f=%pK\n",
+ bottom_half_handler,
+ bottom_half,
+ bottom_half_enqueue_func);
+ return -EINVAL;
+ }
+
+ if (priority >= CAM_IRQ_PRIORITY_MAX) {
+ pr_err("Invalid priority=%u, max=%u\n", priority,
+ CAM_IRQ_PRIORITY_MAX);
+ return -EINVAL;
+ }
+
+ if (sizeof(evt_bit_mask_arr) !=
+ sizeof(uint32_t) * controller->num_registers) {
+ pr_err("Invalid evt_mask size = %lu expected = %lu\n",
+ sizeof(evt_bit_mask_arr),
+ sizeof(uint32_t) * controller->num_registers);
+ return -EINVAL;
+ }
+
+ evt_handler = kzalloc(sizeof(struct cam_irq_evt_handler), GFP_KERNEL);
+ if (!evt_handler) {
+ CDBG("Error allocating hlist_node\n");
+ return -ENOMEM;
+ }
+
+ evt_handler->evt_bit_mask_arr = kzalloc(sizeof(uint32_t) *
+ controller->num_registers, GFP_KERNEL);
+ if (!evt_handler->evt_bit_mask_arr) {
+ CDBG("Error allocating hlist_node\n");
+ rc = -ENOMEM;
+ goto free_evt_handler;
+ }
+
+ INIT_LIST_HEAD(&evt_handler->list_node);
+ INIT_LIST_HEAD(&evt_handler->th_list_node);
+
+ for (i = 0; i < controller->num_registers; i++)
+ evt_handler->evt_bit_mask_arr[i] = evt_bit_mask_arr[i];
+
+ evt_handler->priority = priority;
+ evt_handler->handler_priv = handler_priv;
+ evt_handler->top_half_handler = top_half_handler;
+ evt_handler->bottom_half_handler = bottom_half_handler;
+ evt_handler->bottom_half = bottom_half;
+ evt_handler->bottom_half_enqueue_func = bottom_half_enqueue_func;
+ evt_handler->index = controller->hdl_idx++;
+ if (controller->hdl_idx > 0x3FFFFFFF)
+ controller->hdl_idx = 1;
+
+ write_lock_irqsave(&controller->rw_lock, flags);
+ for (i = 0; i < controller->num_registers; i++) {
+ controller->irq_register_arr[i].top_half_enable_mask[priority]
+ |= evt_bit_mask_arr[i];
+
+ irq_mask = cam_io_r_mb(controller->mem_base +
+ controller->irq_register_arr[i].mask_reg_offset);
+ irq_mask |= evt_bit_mask_arr[i];
+
+ cam_io_w_mb(irq_mask, controller->mem_base +
+ controller->irq_register_arr[i].mask_reg_offset);
+ }
+ write_unlock_irqrestore(&controller->rw_lock, flags);
+
+ list_add_tail(&evt_handler->list_node,
+ &controller->evt_handler_list_head);
+ list_add_tail(&evt_handler->th_list_node,
+ &controller->th_list_head[priority]);
+
+ return evt_handler->index;
+
+free_evt_handler:
+ kfree(evt_handler);
+ evt_handler = NULL;
+
+ return rc;
+}
+
+int cam_irq_controller_unsubscribe_irq(void *irq_controller,
+ uint32_t handle)
+{
+ struct cam_irq_controller *controller = irq_controller;
+ struct cam_irq_evt_handler *evt_handler = NULL;
+ struct cam_irq_evt_handler *evt_handler_temp;
+ uint32_t i;
+ uint32_t found = 0;
+ uint32_t irq_mask;
+ unsigned long flags;
+ int rc = -EINVAL;
+
+ list_for_each_entry_safe(evt_handler, evt_handler_temp,
+ &controller->evt_handler_list_head, list_node) {
+ if (evt_handler->index == handle) {
+ CDBG("unsubscribe item %d\n", handle);
+ list_del_init(&evt_handler->list_node);
+ list_del_init(&evt_handler->th_list_node);
+ found = 1;
+ rc = 0;
+ break;
+ }
+ }
+
+ if (found) {
+ write_lock_irqsave(&controller->rw_lock, flags);
+ for (i = 0; i < controller->num_registers; i++) {
+ controller->irq_register_arr[i].
+ top_half_enable_mask[evt_handler->priority] &=
+ ~(evt_handler->evt_bit_mask_arr[i]);
+
+ irq_mask = cam_io_r_mb(controller->mem_base +
+ controller->irq_register_arr[i].
+ mask_reg_offset);
+ irq_mask &= ~(evt_handler->evt_bit_mask_arr[i]);
+
+ cam_io_w_mb(irq_mask, controller->mem_base +
+ controller->irq_register_arr[i].
+ mask_reg_offset);
+
+ /* Clear the IRQ bits of this handler */
+ cam_io_w_mb(evt_handler->evt_bit_mask_arr[i],
+ controller->mem_base +
+ controller->irq_register_arr[i].
+ clear_reg_offset);
+ if (controller->global_clear_offset)
+ cam_io_w_mb(
+ controller->global_clear_bitmask,
+ controller->mem_base +
+ controller->global_clear_offset);
+ }
+ write_unlock_irqrestore(&controller->rw_lock, flags);
+
+ kfree(evt_handler->evt_bit_mask_arr);
+ kfree(evt_handler);
+ }
+
+ return rc;
+}
+
+/**
+ * cam_irq_controller_match_bit_mask()
+ *
+ * @Brief: This function checks if any of the enabled IRQ bits
+ * for a certain handler is Set in the Status values
+ * of the controller.
+ *
+ * @controller: IRQ Controller structure
+ * @evt_handler: Event handler structure
+ *
+ * @Return: True: If any interested IRQ Bit is Set
+ * False: Otherwise
+ */
+static bool cam_irq_controller_match_bit_mask(
+ struct cam_irq_controller *controller,
+ struct cam_irq_evt_handler *evt_handler)
+{
+ int i;
+
+ for (i = 0; i < controller->num_registers; i++) {
+ if (evt_handler->evt_bit_mask_arr[i] &
+ controller->irq_status_arr[i])
+ return true;
+ }
+
+ return false;
+}
+
+static void cam_irq_controller_th_processing(
+ struct cam_irq_controller *controller,
+ struct list_head *th_list_head)
+{
+ struct cam_irq_evt_handler *evt_handler = NULL;
+ struct cam_irq_th_payload th_payload;
+ bool is_irq_match;
+ int rc = -EINVAL;
+
+ CDBG("Enter\n");
+
+ if (list_empty(th_list_head))
+ return;
+
+ list_for_each_entry(evt_handler, th_list_head, th_list_node) {
+ is_irq_match = cam_irq_controller_match_bit_mask(controller,
+ evt_handler);
+
+ if (!is_irq_match)
+ continue;
+
+ CDBG("match found\n");
+
+ cam_irq_th_payload_init(&th_payload);
+ th_payload.handler_priv = evt_handler->handler_priv;
+ th_payload.num_registers = controller->num_registers;
+ th_payload.evt_status_arr = controller->irq_status_arr;
+
+ /*
+ * irq_status_arr[0] is dummy argument passed. the entire
+ * status array is passed in th_payload.
+ */
+ if (evt_handler->top_half_handler)
+ rc = evt_handler->top_half_handler(
+ controller->irq_status_arr[0],
+ (void *)&th_payload);
+
+
+ if (!rc && evt_handler->bottom_half_handler) {
+ CDBG("Enqueuing bottom half\n");
+ if (evt_handler->bottom_half_enqueue_func) {
+ evt_handler->bottom_half_enqueue_func(
+ evt_handler->bottom_half,
+ evt_handler->handler_priv,
+ th_payload.evt_payload_priv,
+ evt_handler->bottom_half_handler);
+ }
+ }
+ }
+
+ CDBG("Exit\n");
+}
+
+irqreturn_t cam_irq_controller_handle_irq(int irq_num, void *priv)
+{
+ struct cam_irq_controller *controller = priv;
+ int i, j;
+ bool need_th_processing[CAM_IRQ_PRIORITY_MAX] = {false};
+
+ if (!controller)
+ return IRQ_NONE;
+
+ read_lock(&controller->rw_lock);
+ for (i = 0; i < controller->num_registers; i++) {
+ controller->irq_status_arr[i] = cam_io_r_mb(
+ controller->mem_base +
+ controller->irq_register_arr[i].status_reg_offset);
+ cam_io_w_mb(controller->irq_status_arr[i],
+ controller->mem_base +
+ controller->irq_register_arr[i].clear_reg_offset);
+ CDBG("Read irq status%d = 0x%x\n", i,
+ controller->irq_status_arr[i]);
+ for (j = 0; j < CAM_IRQ_PRIORITY_MAX; j++) {
+ if (controller->irq_register_arr[i].
+ top_half_enable_mask[j] &
+ controller->irq_status_arr[i])
+ need_th_processing[j] = true;
+ CDBG("i %d j %d need_th_processing = %d\n",
+ i, j, need_th_processing[j]);
+ }
+ }
+ read_unlock(&controller->rw_lock);
+
+ CDBG("Status Registers read Successful\n");
+
+ if (controller->global_clear_offset)
+ cam_io_w_mb(controller->global_clear_bitmask,
+ controller->mem_base + controller->global_clear_offset);
+
+ CDBG("Status Clear done\n");
+
+ for (i = 0; i < CAM_IRQ_PRIORITY_MAX; i++) {
+ if (need_th_processing[i]) {
+ CDBG("%s: Invoke TH processing\n", __func__);
+ cam_irq_controller_th_processing(controller,
+ &controller->th_list_head[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h
new file mode 100644
index 0000000..2f88a8b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h
@@ -0,0 +1,218 @@
+/* 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.
+ */
+
+#ifndef _CAM_IRQ_CONTROLLER_H_
+#define _CAM_IRQ_CONTROLLER_H_
+
+#include <linux/interrupt.h>
+
+#define CAM_IRQ_BITS_PER_REGISTER 32
+
+/*
+ * enum cam_irq_priority_level:
+ * @Brief: Priority levels for IRQ events.
+ * Priority_0 events will be serviced before
+ * Priority_1 if they these bits are set in the same
+ * Status Read. And so on upto Priority_4.
+ *
+ * Default Priority is Priority_4.
+ */
+enum cam_irq_priority_level {
+ CAM_IRQ_PRIORITY_0,
+ CAM_IRQ_PRIORITY_1,
+ CAM_IRQ_PRIORITY_2,
+ CAM_IRQ_PRIORITY_3,
+ CAM_IRQ_PRIORITY_4,
+ CAM_IRQ_PRIORITY_MAX,
+};
+
+/*
+ * struct cam_irq_register_set:
+ * @Brief: Structure containing offsets of IRQ related
+ * registers belonging to a Set
+ *
+ * @mask_reg_offset: Offset of IRQ MASK register
+ * @clear_reg_offset: Offset of IRQ CLEAR register
+ * @status_reg_offset: Offset of IRQ STATUS register
+ */
+struct cam_irq_register_set {
+ uint32_t mask_reg_offset;
+ uint32_t clear_reg_offset;
+ uint32_t status_reg_offset;
+};
+
+/*
+ * struct cam_irq_controller_reg_info:
+ * @Brief: Structure describing the IRQ registers
+ *
+ * @num_registers: Number of sets(mask/clear/status) of IRQ registers
+ * @irq_reg_set: Array of Register Set Offsets.
+ * Length of array = num_registers
+ * @global_clear_offset: Offset of Global IRQ Clear register. This register
+ * contains the BIT that needs to be set for the CLEAR
+ * to take effect
+ * @global_clear_bitmask: Bitmask needed to be used in Global Clear register
+ * for Clear IRQ cmd to take effect
+ */
+struct cam_irq_controller_reg_info {
+ uint32_t num_registers;
+ struct cam_irq_register_set *irq_reg_set;
+ uint32_t global_clear_offset;
+ uint32_t global_clear_bitmask;
+};
+
+/*
+ * struct cam_irq_th_payload:
+ * @Brief: Event payload structure. This structure will be
+ * passed to the Top Half handler functions.
+ *
+ * @handler_priv: Private Data of handling object set when
+ * subscribing to IRQ event.
+ * @num_registers: Length of evt_bit_mask Array below
+ * @evt_status_arr: Array of Status bitmask read from registers.
+ * Length of array = num_registers
+ * @evt_payload_priv: Private payload pointer which can be set by Top
+ * Half handler for use in Bottom Half.
+ */
+struct cam_irq_th_payload {
+ void *handler_priv;
+ uint32_t num_registers;
+ uint32_t *evt_status_arr;
+ void *evt_payload_priv;
+};
+
+/*
+ * cam_irq_th_payload_init()
+ *
+ * @brief: Initialize the top half payload structure
+ *
+ * @th_payload: Top Half payload structure to Initialize
+ *
+ * @return: Void
+ */
+static inline void cam_irq_th_payload_init(
+ struct cam_irq_th_payload *th_payload) {
+ th_payload->handler_priv = NULL;
+ th_payload->evt_payload_priv = NULL;
+ th_payload->evt_status_arr = NULL;
+}
+
+typedef int (*CAM_IRQ_HANDLER_TOP_HALF)(uint32_t evt_id,
+ struct cam_irq_th_payload *th_payload);
+
+typedef int (*CAM_IRQ_HANDLER_BOTTOM_HALF)(void *handler_priv,
+ void *evt_payload_priv);
+
+typedef int (*CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC)(void *bottom_half,
+ void *handler_priv, void *evt_payload_priv,
+ CAM_IRQ_HANDLER_BOTTOM_HALF);
+
+/*
+ * cam_irq_controller_init()
+ *
+ * @brief: Create and Initialize IRQ Controller.
+ *
+ * @name: Name of IRQ Controller block
+ * @mem_base: Mapped base address of register space to which
+ * register offsets are added to access registers
+ * @register_info: Register Info structure associated with this Controller
+ * @irq_controller: Pointer to IRQ Controller that will be filled if
+ * initialization is successful
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_irq_controller_init(const char *name,
+ void __iomem *mem_base,
+ struct cam_irq_controller_reg_info *register_info,
+ void **irq_controller);
+
+/*
+ * cam_irq_controller_subscribe_irq()
+ *
+ * @brief: Subscribe to certain IRQ events.
+ *
+ * @irq_controller: Pointer to IRQ Controller that controls this event IRQ
+ * @priority: Priority level of these events used if multiple events
+ * are SET in the Status Register
+ * @evt_bit_mask_arr: evt_bit_mask that has the bits set for IRQs to
+ * subscribe for
+ * @handler_priv: Private data that will be passed to the Top/Bottom Half
+ * handler function
+ * @top_half_handler: Top half Handler callback function
+ * @bottom_half_handler: Bottom half Handler callback function
+ * @bottom_half: Pointer to bottom_half implementation on which to
+ * enqueue the event for further handling
+ * @bottom_half_enqueue_func:
+ * Function used to enqueue the bottom_half event
+ *
+ * @return: Positive: Success. Value represents handle which is
+ * to be used to unsubscribe
+ * Negative: Failure
+ */
+int cam_irq_controller_subscribe_irq(void *irq_controller,
+ enum cam_irq_priority_level priority,
+ uint32_t *evt_bit_mask_arr,
+ void *handler_priv,
+ CAM_IRQ_HANDLER_TOP_HALF top_half_handler,
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler,
+ void *bottom_half,
+ CAM_IRQ_BOTTOM_HALF_ENQUEUE_FUNC bottom_half_enqueue_func);
+
+/*
+ * cam_irq_controller_unsubscribe_irq()
+ *
+ * @brief: Unsubscribe to IRQ events previously subscribed to.
+ *
+ * @irq_controller: Pointer to IRQ Controller that controls this event IRQ
+ * @handle: Handle returned on successful subscribe used to
+ * identify the handler object
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_irq_controller_unsubscribe_irq(void *irq_controller,
+ uint32_t handle);
+
+/*
+ * cam_irq_controller_deinit()
+ *
+ * @brief: Deinitialize IRQ Controller.
+ *
+ * @irq_controller: Pointer to IRQ Controller that needs to be
+ * deinitialized
+ *
+ * @return: 0: Success
+ * Negative: Failure
+ */
+int cam_irq_controller_deinit(void **irq_controller);
+
+/*
+ * cam_irq_controller_handle_irq()
+ *
+ * @brief: Function that should be registered with the IRQ line.
+ * This is the first function to be called when the IRQ
+ * is fired. It will read the Status register and Clear
+ * the IRQ bits. It will then call the top_half handlers
+ * and enqueue the result to bottom_half.
+ *
+ * @irq_num: Number of IRQ line that was set that lead to this
+ * function being called
+ * @priv: Private data registered with request_irq is passed back
+ * here. This private data should be the irq_controller
+ * structure.
+ *
+ * @return: IRQ_HANDLED/IRQ_NONE
+ */
+irqreturn_t cam_irq_controller_handle_irq(int irq_num, void *priv);
+
+#endif /*_CAM_IRQ_CONTROLLER_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/Makefile
index 4e6a06e6..4bf4a0e 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_SPECTRA_CAMERA) += ife_csid_hw/
+obj-$(CONFIG_SPECTRA_CAMERA) += vfe_hw/
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
new file mode 100644
index 0000000..ea34406
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
@@ -0,0 +1,162 @@
+/* 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.
+ */
+
+#ifndef _CAM_ISP_HW_H_
+#define _CAM_ISP_HW_H_
+
+#include <linux/completion.h>
+#include "cam_hw.h"
+#include "cam_soc_util.h"
+#include "cam_irq_controller.h"
+
+/*
+ * struct cam_isp_timestamp:
+ *
+ * @mono_time: Monotonic boot time
+ * @vt_time: AV Timer time
+ * @ticks: Qtimer ticks
+ */
+struct cam_isp_timestamp {
+ struct timeval mono_time;
+ struct timeval vt_time;
+ uint64_t ticks;
+};
+
+/*
+ * cam_isp_hw_get_timestamp()
+ *
+ * @Brief: Get timestamp values
+ *
+ * @time_stamp: Structure that holds different time values
+ *
+ * @Return: Void
+ */
+void cam_isp_hw_get_timestamp(struct cam_isp_timestamp *time_stamp);
+
+enum cam_isp_hw_type {
+ CAM_ISP_HW_TYPE_CSID = 0,
+ CAM_ISP_HW_TYPE_ISPIF = 1,
+ CAM_ISP_HW_TYPE_VFE = 2,
+ CAM_ISP_HW_TYPE_IFE_CSID = 3,
+ CAM_ISP_HW_TYPE_MAX = 4,
+};
+
+enum cam_isp_hw_split_id {
+ CAM_ISP_HW_SPLIT_LEFT = 0,
+ CAM_ISP_HW_SPLIT_RIGHT,
+ CAM_ISP_HW_SPLIT_MAX,
+};
+
+enum cam_isp_hw_sync_mode {
+ CAM_ISP_HW_SYNC_NONE,
+ CAM_ISP_HW_SYNC_MASTER,
+ CAM_ISP_HW_SYNC_SLAVE,
+ CAM_ISP_HW_SYNC_MAX,
+};
+
+enum cam_isp_resource_state {
+ CAM_ISP_RESOURCE_STATE_UNAVAILABLE = 0,
+ CAM_ISP_RESOURCE_STATE_AVAILABLE = 1,
+ CAM_ISP_RESOURCE_STATE_RESERVED = 2,
+ CAM_ISP_RESOURCE_STATE_INIT_HW = 3,
+ CAM_ISP_RESOURCE_STATE_STREAMING = 4,
+};
+
+enum cam_isp_resource_type {
+ CAM_ISP_RESOURCE_UNINT,
+ CAM_ISP_RESOURCE_SRC,
+ CAM_ISP_RESOURCE_CID,
+ CAM_ISP_RESOURCE_PIX_PATH,
+ CAM_ISP_RESOURCE_VFE_IN,
+ CAM_ISP_RESOURCE_VFE_OUT,
+ CAM_ISP_RESOURCE_MAX,
+};
+
+/*
+ * struct cam_isp_resource_node:
+ *
+ * @Brief: Structure representing HW resource object
+ *
+ * @res_type: Resource Type
+ * @res_id: Unique resource ID within res_type objects
+ * for a particular HW
+ * @res_state: State of the resource
+ * @hw_intf: HW Interface of HW to which this resource
+ * belongs
+ * @res_priv: Private data of the resource
+ * @list: list_head node for this resource
+ * @cdm_ops: CDM operation functions
+ * @tasklet_info: Tasklet structure that will be used to
+ * schedule IRQ events related to this resource
+ * @irq_handle: handle returned on subscribing for IRQ event
+ * @start: function pointer to start the HW resource
+ * @stop: function pointer to stop the HW resource
+ * @process_cmd: function pointer for processing commands
+ * specific to the resource
+ * @top_half_handler: Top Half handler function
+ * @bottom_half_handler: Bottom Half handler function
+ */
+struct cam_isp_resource_node {
+ enum cam_isp_resource_type res_type;
+ uint32_t res_id;
+ enum cam_isp_resource_state res_state;
+ struct cam_hw_intf *hw_intf;
+ void *res_priv;
+ struct list_head list;
+ void *cdm_ops;
+ void *tasklet_info;
+ int irq_handle;
+
+ int (*start)(struct cam_isp_resource_node *rsrc_node);
+ int (*stop)(struct cam_isp_resource_node *rsrc_node);
+ int (*process_cmd)(struct cam_isp_resource_node *rsrc_node,
+ uint32_t cmd_type, void *cmd_args, uint32_t arg_size);
+ CAM_IRQ_HANDLER_TOP_HALF top_half_handler;
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler;
+};
+
+/*
+ * struct cam_isp_hw_get_cdm_args:
+ *
+ * @Brief: Contain the command buffer information
+ * to store the CDM commands.
+ *
+ * @res: Resource node
+ * @cmd_buf_addr: Command buffer to store the change base command
+ * @size: Size of the buffer in bytes
+ * @used_bytes: Consumed bytes in the command buffer
+ *
+ */
+struct cam_isp_hw_get_cdm_args {
+ struct cam_isp_resource_node *res;
+ uint32_t *cmd_buf_addr;
+ uint32_t size;
+ uint32_t used_bytes;
+};
+
+/*
+ * struct cam_isp_hw_get_buf_update:
+ *
+ * @Brief: Get cdm commands for buffer updates.
+ *
+ * @ cdm: Command buffer information
+ * @ image_buf: Contain the image buffer information
+ * @ num_buf: Number of buffers in the image_buf array
+ *
+ */
+struct cam_isp_hw_get_buf_update {
+ struct cam_isp_hw_get_cdm_args cdm;
+ uint64_t *image_buf;
+ uint32_t num_buf;
+};
+
+#endif /* _CAM_ISP_HW_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
new file mode 100644
index 0000000..15db6a6
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
@@ -0,0 +1,255 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_HW_INTF_H_
+#define _CAM_VFE_HW_INTF_H_
+
+#include "cam_isp_hw.h"
+
+#define CAM_VFE_HW_NUM_MAX 4
+
+#define VFE_CORE_BASE_IDX 0
+/*
+ * VBIF and BUS do not exist on same HW.
+ * Hence both can be 1 below.
+ */
+#define VFE_VBIF_BASE_IDX 1
+#define VFE_BUS_BASE_IDX 1
+
+enum cam_isp_hw_vfe_in_mux {
+ CAM_ISP_HW_VFE_IN_CAMIF = 0,
+ CAM_ISP_HW_VFE_IN_TESTGEN = 1,
+ CAM_ISP_HW_VFE_IN_BUS_RD = 2,
+ CAM_ISP_HW_VFE_IN_RDI0 = 3,
+ CAM_ISP_HW_VFE_IN_RDI1 = 4,
+ CAM_ISP_HW_VFE_IN_RDI2 = 5,
+ CAM_ISP_HW_VFE_IN_RDI3 = 6,
+ CAM_ISP_HW_VFE_IN_MAX,
+};
+
+enum cam_isp_hw_vfe_core {
+ CAM_ISP_HW_VFE_CORE_0,
+ CAM_ISP_HW_VFE_CORE_1,
+ CAM_ISP_HW_VFE_CORE_2,
+ CAM_ISP_HW_VFE_CORE_3,
+ CAM_ISP_HW_VFE_CORE_MAX,
+};
+
+enum cam_vfe_hw_cmd_type {
+ CAM_VFE_HW_CMD_GET_CHANGE_BASE,
+ CAM_VFE_HW_CMD_GET_BUF_UPDATE,
+ CAM_VFE_HW_CMD_GET_REG_UPDATE,
+ CAM_VFE_HW_CMD_MAX,
+};
+
+enum cam_vfe_hw_irq_status {
+ CAM_VFE_IRQ_STATUS_ERR_COMP = -3,
+ CAM_VFE_IRQ_STATUS_COMP_OWRT = -2,
+ CAM_VFE_IRQ_STATUS_ERR = -1,
+ CAM_VFE_IRQ_STATUS_SUCCESS = 0,
+ CAM_VFE_IRQ_STATUS_MAX,
+};
+
+enum cam_vfe_hw_irq_regs {
+ CAM_IFE_IRQ_CAMIF_REG_STATUS0 = 0,
+ CAM_IFE_IRQ_CAMIF_REG_STATUS1 = 1,
+ CAM_IFE_IRQ_VIOLATION_STATUS = 2,
+ CAM_IFE_IRQ_REGISTERS_MAX,
+};
+
+enum cam_vfe_bus_irq_regs {
+ CAM_IFE_IRQ_BUS_REG_STATUS0 = 0,
+ CAM_IFE_IRQ_BUS_REG_STATUS1 = 1,
+ CAM_IFE_IRQ_BUS_REG_STATUS2 = 2,
+ CAM_IFE_IRQ_BUS_REG_COMP_ERR = 3,
+ CAM_IFE_IRQ_BUS_REG_COMP_OWRT = 4,
+ CAM_IFE_IRQ_BUS_DUAL_COMP_ERR = 5,
+ CAM_IFE_IRQ_BUS_DUAL_COMP_OWRT = 6,
+ CAM_IFE_BUS_IRQ_REGISTERS_MAX,
+};
+
+/*
+ * struct cam_vfe_hw_get_hw_cap:
+ *
+ * @max_width: Max width supported by HW
+ * @max_height: Max height supported by HW
+ * @max_pixel_num: Max Pixel channels available
+ * @max_rdi_num: Max Raw channels available
+ */
+struct cam_vfe_hw_get_hw_cap {
+ uint32_t max_width;
+ uint32_t max_height;
+ uint32_t max_pixel_num;
+ uint32_t max_rdi_num;
+};
+
+/*
+ * struct cam_vfe_hw_vfe_out_acquire_args:
+ *
+ * @rsrc_node: Pointer to Resource Node object, filled if acquire
+ * is successful
+ * @out_port_info: Output Port details to acquire
+ * @unique_id: Unique Identity of Context to associate with this
+ * resource. Used for composite grouping of multiple
+ * resources in the same context
+ * @is_dual: Dual VFE or not
+ * @split_id: In case of Dual VFE, this is Left or Right.
+ * (Default is Left if Single VFE)
+ * @is_master: In case of Dual VFE, this is Master or Slave.
+ * (Default is Master in case of Single VFE)
+ * @dual_slave_core: If Master and Slave exists, HW Index of Slave
+ * @cdm_ops: CDM operations
+ */
+struct cam_vfe_hw_vfe_out_acquire_args {
+ struct cam_isp_resource_node *rsrc_node;
+ struct cam_isp_out_port_info *out_port_info;
+ uint32_t unique_id;
+ uint32_t is_dual;
+ enum cam_isp_hw_split_id split_id;
+ uint32_t is_master;
+ uint32_t dual_slave_core;
+ struct cam_cdm_utils_ops *cdm_ops;
+};
+
+/*
+ * struct cam_vfe_hw_vfe_in_acquire_args:
+ *
+ * @rsrc_node: Pointer to Resource Node object, filled if acquire
+ * is successful
+ * @res_id: Resource ID of resource to acquire if specific,
+ * else CAM_ISP_HW_VFE_IN_MAX
+ * @cdm_ops: CDM operations
+ * @sync_mode: In case of Dual VFE, this is Master or Slave.
+ * (Default is Master in case of Single VFE)
+ * @in_port: Input port details to acquire
+ */
+struct cam_vfe_hw_vfe_in_acquire_args {
+ struct cam_isp_resource_node *rsrc_node;
+ uint32_t res_id;
+ void *cdm_ops;
+ enum cam_isp_hw_sync_mode sync_mode;
+ struct cam_isp_in_port_info *in_port;
+};
+
+/*
+ * struct cam_vfe_acquire_args:
+ *
+ * @rsrc_type: Type of Resource (OUT/IN) to acquire
+ * @tasklet: Tasklet to associate with this resource. This is
+ * used to schedule bottom of IRQ events associated
+ * with this resource.
+ * @vfe_out: Acquire args for VFE_OUT
+ * @vfe_in: Acquire args for VFE_IN
+ */
+struct cam_vfe_acquire_args {
+ enum cam_isp_resource_type rsrc_type;
+ void *tasklet;
+ union {
+ struct cam_vfe_hw_vfe_out_acquire_args vfe_out;
+ struct cam_vfe_hw_vfe_in_acquire_args vfe_in;
+ };
+};
+
+/*
+ * struct cam_vfe_top_irq_evt_payload:
+ *
+ * @Brief: This structure is used to save payload for IRQ
+ * related to VFE_TOP resources
+ *
+ * @list: list_head node for the payload
+ * @core_index: Index of VFE HW that generated this IRQ event
+ * @core_info: Private data of handler in bottom half context
+ * @evt_id: IRQ event
+ * @irq_reg_val: IRQ and Error register values, read when IRQ was
+ * handled
+ * @error_type: Identify different errors
+ * @ts: Timestamp
+ */
+struct cam_vfe_top_irq_evt_payload {
+ struct list_head list;
+ uint32_t core_index;
+ void *core_info;
+ uint32_t evt_id;
+ uint32_t irq_reg_val[CAM_IFE_IRQ_REGISTERS_MAX];
+ uint32_t error_type;
+ struct cam_isp_timestamp ts;
+};
+
+/*
+ * struct cam_vfe_bus_irq_evt_payload:
+ *
+ * @Brief: This structure is used to save payload for IRQ
+ * related to VFE_BUS resources
+ *
+ * @list: list_head node for the payload
+ * @core_index: Index of VFE HW that generated this IRQ event
+ * @core_info: Private data of handler in bottom half context
+ * @evt_id: IRQ event
+ * @irq_reg_val: IRQ and Error register values, read when IRQ was
+ * handled
+ * @error_type: Identify different errors
+ * @ts: Timestamp
+ */
+struct cam_vfe_bus_irq_evt_payload {
+ struct list_head list;
+ uint32_t core_index;
+ void *core_info;
+ uint32_t evt_id;
+ uint32_t irq_reg_val[CAM_IFE_BUS_IRQ_REGISTERS_MAX];
+ uint32_t error_type;
+ struct cam_vfe_bus_ver2_priv *bus_priv;
+ struct cam_isp_timestamp ts;
+};
+
+/*
+ * struct cam_vfe_irq_handler_priv:
+ *
+ * @Brief: This structure is used as private data to
+ * register with IRQ controller. It has information
+ * needed by top half and bottom half.
+ *
+ * @core_index: Index of VFE HW that generated this IRQ event
+ * @core_info: Private data of handler in bottom half context
+ * @mem_base: Mapped base address of the register space
+ * @reset_complete: Completion structure to be signaled if Reset IRQ
+ * is Set
+ */
+struct cam_vfe_irq_handler_priv {
+ uint32_t core_index;
+ void *core_info;
+ void __iomem *mem_base;
+ struct completion *reset_complete;
+};
+
+/*
+ * cam_vfe_hw_init()
+ *
+ * @Brief: Initialize VFE HW device
+ *
+ * @vfe_hw: vfe_hw interface to fill in and return on
+ * successful initialization
+ * @hw_idx: Index of VFE HW
+ */
+int cam_vfe_hw_init(struct cam_hw_intf **vfe_hw, uint32_t hw_idx);
+
+/*
+ * cam_vfe_put_evt_payload()
+ *
+ * @Brief: Put the evt payload back to free list
+ *
+ * @core_info: VFE HW core_info
+ * @evt_payload: Event payload data
+ */
+int cam_vfe_put_evt_payload(void *core_info,
+ struct cam_vfe_top_irq_evt_payload **evt_payload);
+
+#endif /* _CAM_VFE_HW_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile
new file mode 100644
index 0000000..5a67efa
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/Makefile
@@ -0,0 +1,15 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
+
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_soc.o cam_vfe_dev.o cam_vfe_core.o
+obj-$(CONFIG_SPECTRA_CAMERA) += vfe_bus/ vfe_top/ vfe170/
\ No newline at end of file
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
new file mode 100644
index 0000000..488a00b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -0,0 +1,619 @@
+/* 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/delay.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/ratelimit.h>
+#include "cam_tasklet_util.h"
+#include "cam_isp_hw_mgr_intf.h"
+#include "cam_vfe_soc.h"
+#include "cam_vfe_core.h"
+#include "cam_vfe_bus.h"
+#include "cam_vfe_top.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static const char drv_name[] = "vfe";
+
+static uint32_t irq_reg_offset[CAM_IFE_IRQ_REGISTERS_MAX] = {
+ 0x0000006C,
+ 0x00000070,
+ 0x0000007C,
+};
+
+static uint32_t top_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
+ 0x7803FDFF,
+ 0x0FFF7EB3,
+};
+
+static uint32_t top_reset_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
+ 0x80000000,
+ 0x00000000,
+};
+
+static uint32_t bus_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
+ 0x00000200,
+ 0x00000000,
+};
+
+static int cam_vfe_get_evt_payload(struct cam_vfe_hw_core_info *core_info,
+ struct cam_vfe_top_irq_evt_payload **evt_payload)
+{
+ spin_lock(&core_info->spin_lock);
+ if (list_empty(&core_info->free_payload_list)) {
+ *evt_payload = NULL;
+ spin_unlock(&core_info->spin_lock);
+ pr_err_ratelimited("No free payload, core info 0x%x\n",
+ core_info->cpas_handle);
+ return -ENODEV;
+ }
+
+ *evt_payload = list_first_entry(&core_info->free_payload_list,
+ struct cam_vfe_top_irq_evt_payload, list);
+ list_del_init(&(*evt_payload)->list);
+ spin_unlock(&core_info->spin_lock);
+
+ return 0;
+}
+
+int cam_vfe_put_evt_payload(void *core_info,
+ struct cam_vfe_top_irq_evt_payload **evt_payload)
+{
+ struct cam_vfe_hw_core_info *vfe_core_info = core_info;
+ unsigned long flags;
+
+ 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;
+ }
+
+ spin_lock_irqsave(&vfe_core_info->spin_lock, flags);
+ list_add_tail(&(*evt_payload)->list, &vfe_core_info->free_payload_list);
+ spin_unlock_irqrestore(&vfe_core_info->spin_lock, flags);
+
+ *evt_payload = NULL;
+ return 0;
+}
+
+int cam_vfe_get_hw_caps(void *hw_priv, void *get_hw_cap_args, uint32_t arg_size)
+{
+ struct cam_hw_info *vfe_dev = hw_priv;
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ int rc = 0;
+
+ CDBG("Enter\n");
+ if (!hw_priv) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ core_info = (struct cam_vfe_hw_core_info *)vfe_dev->core_info;
+
+ if (core_info->vfe_top->hw_ops.get_hw_caps)
+ core_info->vfe_top->hw_ops.get_hw_caps(
+ core_info->vfe_top->top_priv,
+ get_hw_cap_args, arg_size);
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+int cam_vfe_reset_irq_top_half(uint32_t evt_id,
+ struct cam_irq_th_payload *th_payload)
+{
+ int32_t rc = -EINVAL;
+ struct cam_vfe_irq_handler_priv *handler_priv;
+
+ handler_priv = th_payload->handler_priv;
+
+ CDBG("Enter\n");
+ CDBG("IRQ status_0 = 0x%x\n", th_payload->evt_status_arr[0]);
+
+ if (th_payload->evt_status_arr[0] & (1<<31)) {
+ CDBG("Calling Complete for RESET CMD\n");
+ complete(handler_priv->reset_complete);
+
+ /*
+ * Clear All IRQs to avoid spurious IRQs immediately
+ * after Reset Done.
+ */
+ cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x64);
+ cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x68);
+ cam_io_w(0x1, handler_priv->mem_base + 0x58);
+
+ rc = 0;
+ }
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+int cam_vfe_init_hw(void *hw_priv, void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ int rc = 0;
+
+ CDBG("Enter\n");
+ if (!hw_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ vfe_hw->open_count++;
+ if (vfe_hw->open_count > 1) {
+ mutex_unlock(&vfe_hw->hw_mutex);
+ CDBG("VFE has already been initialized cnt %d\n",
+ vfe_hw->open_count);
+ return 0;
+ }
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ soc_info = &vfe_hw->soc_info;
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+
+ /* Turn ON Regulators, Clocks and other SOC resources */
+ rc = cam_vfe_enable_soc_resources(soc_info);
+ if (rc) {
+ pr_err("Enable SOC failed\n");
+ rc = -EFAULT;
+ goto decrement_open_cnt;
+ }
+
+ CDBG("Enable soc done\n");
+
+ /* Do HW Reset */
+ rc = cam_vfe_reset(hw_priv, NULL, 0);
+ if (rc) {
+ pr_err("Reset Failed\n");
+ goto disable_soc;
+ }
+
+ return 0;
+
+disable_soc:
+ cam_vfe_disable_soc_resources(soc_info);
+decrement_open_cnt:
+ mutex_lock(&vfe_hw->hw_mutex);
+ vfe_hw->open_count--;
+ mutex_unlock(&vfe_hw->hw_mutex);
+ return rc;
+}
+
+int cam_vfe_deinit_hw(void *hw_priv, void *deinit_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ int rc = 0;
+
+ CDBG("Enter\n");
+ if (!hw_priv) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ if (!vfe_hw->open_count) {
+ mutex_unlock(&vfe_hw->hw_mutex);
+ pr_err("Error! Unbalanced deinit\n");
+ return -EFAULT;
+ }
+ vfe_hw->open_count--;
+ if (vfe_hw->open_count) {
+ mutex_unlock(&vfe_hw->hw_mutex);
+ CDBG("open_cnt non-zero =%d\n", vfe_hw->open_count);
+ return 0;
+ }
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ soc_info = &vfe_hw->soc_info;
+
+ /* Turn OFF Regulators, Clocks and other SOC resources */
+ CDBG("Disable SOC resource\n");
+ rc = cam_vfe_disable_soc_resources(soc_info);
+ if (rc)
+ pr_err("Disable SOC failed\n");
+
+ vfe_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+int cam_vfe_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size)
+{
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ int rc;
+
+ CDBG("Enter\n");
+
+ if (!hw_priv) {
+ pr_err("Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ soc_info = &vfe_hw->soc_info;
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+
+ core_info->irq_payload.core_index = soc_info->index;
+ core_info->irq_payload.mem_base =
+ vfe_hw->soc_info.reg_map[VFE_CORE_BASE_IDX].mem_base;
+ core_info->irq_payload.core_info = core_info;
+ core_info->irq_payload.reset_complete = &vfe_hw->hw_complete;
+
+ core_info->irq_handle = cam_irq_controller_subscribe_irq(
+ core_info->vfe_irq_controller, CAM_IRQ_PRIORITY_0,
+ top_reset_irq_reg_mask, &core_info->irq_payload,
+ cam_vfe_reset_irq_top_half, NULL, NULL, NULL);
+ if (core_info->irq_handle < 0) {
+ pr_err("subscribe irq controller failed\n");
+ return -EFAULT;
+ }
+
+ reinit_completion(&vfe_hw->hw_complete);
+
+ CDBG("calling RESET\n");
+ core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv, NULL, 0);
+ CDBG("waiting for vfe reset complete\n");
+ /* Wait for Completion or Timeout of 500ms */
+ rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500);
+ if (!rc)
+ pr_err("Error! Reset Timeout\n");
+
+ CDBG("reset complete done (%d)\n", rc);
+
+ rc = cam_irq_controller_unsubscribe_irq(
+ core_info->vfe_irq_controller, core_info->irq_handle);
+ if (rc)
+ pr_err("Error! Unsubscribe failed\n");
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+void cam_isp_hw_get_timestamp(struct cam_isp_timestamp *time_stamp)
+{
+ struct timespec ts;
+
+ get_monotonic_boottime(&ts);
+ time_stamp->mono_time.tv_sec = ts.tv_sec;
+ time_stamp->mono_time.tv_usec = ts.tv_nsec/1000;
+}
+
+
+int cam_vfe_irq_top_half(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_top_irq_evt_payload *evt_payload;
+
+ handler_priv = th_payload->handler_priv;
+
+ CDBG("IRQ status_0 = %x\n", th_payload->evt_status_arr[0]);
+ CDBG("IRQ status_1 = %x\n", th_payload->evt_status_arr[1]);
+
+ rc = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload);
+ if (rc) {
+ pr_err_ratelimited("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->evt_id = evt_id;
+
+ for (i = 0; i < th_payload->num_registers; i++)
+ evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i];
+
+ for (; i < CAM_IFE_IRQ_REGISTERS_MAX; i++) {
+ evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base +
+ irq_reg_offset[i]);
+ }
+ CDBG("Violation status = %x\n", evt_payload->irq_reg_val[2]);
+
+ /*
+ * need to handle overflow condition here, otherwise irq storm
+ * will block everything.
+ */
+ if (evt_payload->irq_reg_val[1]) {
+ pr_err("Mask all the interrupts\n");
+ cam_io_w(0, handler_priv->mem_base + 0x60);
+ cam_io_w(0, handler_priv->mem_base + 0x5C);
+
+ evt_payload->error_type = CAM_ISP_HW_ERROR_OVERFLOW;
+ }
+
+ th_payload->evt_payload_priv = evt_payload;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+int cam_vfe_reserve(void *hw_priv, void *reserve_args, uint32_t arg_size)
+{
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_vfe_acquire_args *acquire;
+ int rc = -ENODEV;
+
+
+ if (!hw_priv || !reserve_args || (arg_size !=
+ sizeof(struct cam_vfe_acquire_args))) {
+ pr_err("Invalid input arguments\n");
+ return -EINVAL;
+ }
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+ acquire = (struct cam_vfe_acquire_args *)reserve_args;
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ if (acquire->rsrc_type == CAM_ISP_RESOURCE_VFE_IN)
+ rc = core_info->vfe_top->hw_ops.reserve(
+ core_info->vfe_top->top_priv,
+ acquire,
+ sizeof(acquire));
+ else if (acquire->rsrc_type == CAM_ISP_RESOURCE_VFE_OUT)
+ rc = core_info->vfe_bus->acquire_resource(
+ core_info->vfe_bus->bus_priv, acquire);
+ else
+ pr_err("Invalid res type:%d\n", acquire->rsrc_type);
+
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ return rc;
+}
+
+
+int cam_vfe_release(void *hw_priv, void *release_args, uint32_t arg_size)
+{
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_isp_resource_node *isp_res;
+ int rc = -ENODEV;
+
+ if (!hw_priv || !release_args ||
+ (arg_size != sizeof(struct cam_isp_resource_node))) {
+ pr_err("Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+ isp_res = (struct cam_isp_resource_node *) release_args;
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN)
+ rc = core_info->vfe_top->hw_ops.release(
+ core_info->vfe_top->top_priv, isp_res,
+ sizeof(struct cam_isp_resource_node));
+ else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT)
+ rc = core_info->vfe_bus->release_resource(
+ core_info->vfe_bus->bus_priv, isp_res);
+ else
+ pr_err("Invalid res type:%d\n", isp_res->res_type);
+
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ return rc;
+}
+
+
+int cam_vfe_start(void *hw_priv, void *start_args, uint32_t arg_size)
+{
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_isp_resource_node *isp_res;
+ int rc = -ENODEV;
+
+ if (!hw_priv || !start_args ||
+ (arg_size != sizeof(struct cam_isp_resource_node))) {
+ pr_err("Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+ isp_res = (struct cam_isp_resource_node *)start_args;
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) {
+ isp_res->irq_handle = cam_irq_controller_subscribe_irq(
+ core_info->vfe_irq_controller, CAM_IRQ_PRIORITY_2,
+ top_irq_reg_mask, &core_info->irq_payload,
+ cam_vfe_irq_top_half, NULL,
+ isp_res->tasklet_info, cam_tasklet_enqueue_cmd);
+ if (isp_res->irq_handle > 0)
+ rc = core_info->vfe_top->hw_ops.start(
+ core_info->vfe_top->top_priv, isp_res,
+ sizeof(struct cam_isp_resource_node));
+ else
+ pr_err("Error! subscribe irq controller failed\n");
+ } else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
+ isp_res->irq_handle = cam_irq_controller_subscribe_irq(
+ core_info->vfe_irq_controller, CAM_IRQ_PRIORITY_1,
+ bus_irq_reg_mask, &core_info->irq_payload,
+ core_info->vfe_bus->top_half_handler,
+ NULL,
+ isp_res->tasklet_info, cam_tasklet_enqueue_cmd);
+ if (isp_res->irq_handle > 0)
+ rc = core_info->vfe_bus->start_resource(isp_res);
+ else
+ pr_err("Error! subscribe irq controller failed\n");
+ } else {
+ pr_err("Invalid res type:%d\n", isp_res->res_type);
+ }
+
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ return rc;
+}
+
+int cam_vfe_stop(void *hw_priv, void *stop_args, uint32_t arg_size)
+{
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_isp_resource_node *isp_res;
+ int rc = -EINVAL;
+
+ if (!hw_priv || !stop_args ||
+ (arg_size != sizeof(struct cam_isp_resource_node))) {
+ pr_err("Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+ isp_res = (struct cam_isp_resource_node *)stop_args;
+
+ mutex_lock(&vfe_hw->hw_mutex);
+ if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) {
+ cam_irq_controller_unsubscribe_irq(
+ core_info->vfe_irq_controller, isp_res->irq_handle);
+ rc = core_info->vfe_top->hw_ops.stop(
+ core_info->vfe_top->top_priv, isp_res,
+ sizeof(struct cam_isp_resource_node));
+ } else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
+ cam_irq_controller_unsubscribe_irq(
+ core_info->vfe_irq_controller, isp_res->irq_handle);
+ rc = core_info->vfe_bus->stop_resource(isp_res);
+ } else {
+ pr_err("Invalid res type:%d\n", isp_res->res_type);
+ }
+
+ mutex_unlock(&vfe_hw->hw_mutex);
+
+ return rc;
+}
+
+int cam_vfe_read(void *hw_priv, void *read_args, uint32_t arg_size)
+{
+ return -EPERM;
+}
+
+int cam_vfe_write(void *hw_priv, void *write_args, uint32_t arg_size)
+{
+ return -EPERM;
+}
+
+int cam_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *vfe_hw = hw_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_vfe_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ if (!hw_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ soc_info = &vfe_hw->soc_info;
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+ hw_info = core_info->vfe_hw_info;
+
+ switch (cmd_type) {
+ case CAM_VFE_HW_CMD_GET_CHANGE_BASE:
+ case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+ rc = core_info->vfe_top->hw_ops.process_cmd(
+ core_info->vfe_top->top_priv, cmd_type, cmd_args,
+ arg_size);
+
+ break;
+ case CAM_VFE_HW_CMD_GET_BUF_UPDATE:
+ rc = core_info->vfe_bus->process_cmd(
+ core_info->vfe_bus->bus_priv, cmd_type, cmd_args,
+ arg_size);
+ break;
+
+ default:
+ pr_err("Invalid cmd type:%d\n", cmd_type);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+irqreturn_t cam_vfe_irq(int irq_num, void *data)
+{
+ struct cam_hw_info *vfe_hw;
+ struct cam_vfe_hw_core_info *core_info;
+
+ if (!data)
+ return IRQ_NONE;
+
+ vfe_hw = (struct cam_hw_info *)data;
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+
+ return cam_irq_controller_handle_irq(irq_num,
+ core_info->vfe_irq_controller);
+}
+
+int cam_vfe_core_init(struct cam_vfe_hw_core_info *core_info,
+ struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ struct cam_vfe_hw_info *vfe_hw_info)
+{
+ int rc = -EINVAL;
+ int i;
+
+ CDBG("Enter");
+
+ rc = cam_irq_controller_init(drv_name,
+ CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX),
+ vfe_hw_info->irq_reg_info, &core_info->vfe_irq_controller);
+ if (rc) {
+ pr_err("Error! cam_irq_controller_init failed\n");
+ return rc;
+ }
+
+ rc = cam_vfe_top_init(vfe_hw_info->top_version,
+ soc_info, hw_intf, vfe_hw_info->top_hw_info,
+ &core_info->vfe_top);
+ if (rc) {
+ pr_err("Error! cam_vfe_top_init failed\n");
+ return rc;
+ }
+
+ rc = cam_vfe_bus_init(vfe_hw_info->bus_version,
+ soc_info->reg_map[0].mem_base, hw_intf,
+ vfe_hw_info->bus_hw_info, NULL, &core_info->vfe_bus);
+ if (rc) {
+ pr_err("Error! cam_vfe_bus_init failed\n");
+ return rc;
+ }
+
+ INIT_LIST_HEAD(&core_info->free_payload_list);
+ for (i = 0; i < CAM_VFE_EVT_MAX; i++) {
+ INIT_LIST_HEAD(&core_info->evt_payload[i].list);
+ list_add_tail(&core_info->evt_payload[i].list,
+ &core_info->free_payload_list);
+ }
+
+ spin_lock_init(&core_info->spin_lock);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
new file mode 100644
index 0000000..92e322b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
@@ -0,0 +1,92 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_CORE_H_
+#define _CAM_VFE_CORE_H_
+
+#include <linux/spinlock.h>
+#include "cam_hw_intf.h"
+#include "cam_vfe_top.h"
+#include "cam_vfe_bus.h"
+#include "cam_vfe_hw_intf.h"
+
+struct cam_vfe_hw_info {
+ struct cam_irq_controller_reg_info *irq_reg_info;
+
+ uint32_t bus_version;
+ void *bus_hw_info;
+
+ uint32_t top_version;
+ void *top_hw_info;
+ uint32_t camif_version;
+ void *camif_reg;
+
+ uint32_t testgen_version;
+ void *testgen_reg;
+
+ uint32_t num_qos_settings;
+ struct cam_isp_reg_val_pair *qos_settings;
+
+ uint32_t num_ds_settings;
+ struct cam_isp_reg_val_pair *ds_settings;
+
+ uint32_t num_vbif_settings;
+ struct cam_isp_reg_val_pair *vbif_settings;
+};
+
+#define CAM_VFE_EVT_MAX 128
+
+struct cam_vfe_hw_core_info {
+ struct cam_vfe_hw_info *vfe_hw_info;
+ void *vfe_irq_controller;
+ struct cam_vfe_top *vfe_top;
+ struct cam_vfe_bus *vfe_bus;
+
+ struct cam_vfe_top_irq_evt_payload evt_payload[CAM_VFE_EVT_MAX];
+ struct list_head free_payload_list;
+ struct cam_vfe_irq_handler_priv irq_payload;
+ uint32_t cpas_handle;
+ int irq_handle;
+ spinlock_t spin_lock;
+};
+
+int cam_vfe_get_hw_caps(void *device_priv,
+ void *get_hw_cap_args, uint32_t arg_size);
+int cam_vfe_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size);
+int cam_vfe_deinit_hw(void *hw_priv,
+ void *deinit_hw_args, uint32_t arg_size);
+int cam_vfe_reset(void *device_priv,
+ void *reset_core_args, uint32_t arg_size);
+int cam_vfe_reserve(void *device_priv,
+ void *reserve_args, uint32_t arg_size);
+int cam_vfe_release(void *device_priv,
+ void *reserve_args, uint32_t arg_size);
+int cam_vfe_start(void *device_priv,
+ void *start_args, uint32_t arg_size);
+int cam_vfe_stop(void *device_priv,
+ void *stop_args, uint32_t arg_size);
+int cam_vfe_read(void *device_priv,
+ void *read_args, uint32_t arg_size);
+int cam_vfe_write(void *device_priv,
+ void *write_args, uint32_t arg_size);
+int cam_vfe_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+
+irqreturn_t cam_vfe_irq(int irq_num, void *data);
+
+int cam_vfe_core_init(struct cam_vfe_hw_core_info *core_info,
+ struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ struct cam_vfe_hw_info *vfe_hw_info);
+
+#endif /* _CAM_VFE_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c
new file mode 100644
index 0000000..40279ae
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.c
@@ -0,0 +1,140 @@
+/* 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/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include "cam_vfe_dev.h"
+#include "cam_vfe_core.h"
+#include "cam_vfe_soc.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static struct cam_hw_intf *cam_vfe_hw_list[CAM_VFE_HW_NUM_MAX] = {0, 0, 0, 0};
+
+int cam_vfe_probe(struct platform_device *pdev)
+{
+ struct cam_hw_info *vfe_hw = NULL;
+ struct cam_hw_intf *vfe_hw_intf = NULL;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_vfe_hw_core_info *core_info = NULL;
+ struct cam_vfe_hw_info *hw_info = NULL;
+ int rc = 0;
+
+ vfe_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!vfe_hw_intf) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &vfe_hw_intf->hw_idx);
+
+ vfe_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!vfe_hw) {
+ rc = -ENOMEM;
+ goto free_vfe_hw_intf;
+ }
+ vfe_hw->soc_info.pdev = pdev;
+ vfe_hw_intf->hw_priv = vfe_hw;
+ vfe_hw_intf->hw_ops.get_hw_caps = cam_vfe_get_hw_caps;
+ vfe_hw_intf->hw_ops.init = cam_vfe_init_hw;
+ vfe_hw_intf->hw_ops.deinit = cam_vfe_deinit_hw;
+ vfe_hw_intf->hw_ops.reset = cam_vfe_reset;
+ vfe_hw_intf->hw_ops.reserve = cam_vfe_reserve;
+ vfe_hw_intf->hw_ops.release = cam_vfe_release;
+ vfe_hw_intf->hw_ops.start = cam_vfe_start;
+ vfe_hw_intf->hw_ops.stop = cam_vfe_stop;
+ vfe_hw_intf->hw_ops.read = cam_vfe_read;
+ vfe_hw_intf->hw_ops.write = cam_vfe_write;
+ vfe_hw_intf->hw_ops.process_cmd = cam_vfe_process_cmd;
+ vfe_hw_intf->hw_type = CAM_ISP_HW_TYPE_VFE;
+
+ CDBG("type %d index %d\n", vfe_hw_intf->hw_type, vfe_hw_intf->hw_idx);
+
+ platform_set_drvdata(pdev, vfe_hw_intf);
+
+ vfe_hw->core_info = kzalloc(sizeof(struct cam_vfe_hw_core_info),
+ GFP_KERNEL);
+ if (!vfe_hw->core_info) {
+ CDBG("Failed to alloc for core\n");
+ rc = -ENOMEM;
+ goto free_vfe_hw;
+ }
+ core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev) {
+ pr_err("Of_match Failed\n");
+ rc = -EINVAL;
+ goto free_core_info;
+ }
+ hw_info = (struct cam_vfe_hw_info *)match_dev->data;
+ core_info->vfe_hw_info = hw_info;
+
+ rc = cam_vfe_init_soc_resources(&vfe_hw->soc_info, cam_vfe_irq,
+ vfe_hw);
+ if (rc < 0) {
+ pr_err("Failed to init soc\n");
+ goto free_core_info;
+ }
+
+ rc = cam_vfe_core_init(core_info, &vfe_hw->soc_info,
+ vfe_hw_intf, hw_info);
+ if (rc < 0) {
+ pr_err("Failed to init core\n");
+ goto deinit_soc;
+ }
+
+ vfe_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+ mutex_init(&vfe_hw->hw_mutex);
+ spin_lock_init(&vfe_hw->hw_lock);
+ init_completion(&vfe_hw->hw_complete);
+
+ if (vfe_hw_intf->hw_idx < CAM_VFE_HW_NUM_MAX)
+ cam_vfe_hw_list[vfe_hw_intf->hw_idx] = vfe_hw_intf;
+
+ cam_vfe_init_hw(vfe_hw, NULL, 0);
+ cam_vfe_deinit_hw(vfe_hw, NULL, 0);
+
+ CDBG("VFE%d probe successful\n", vfe_hw_intf->hw_idx);
+
+ return rc;
+
+deinit_soc:
+free_core_info:
+ kfree(vfe_hw->core_info);
+free_vfe_hw:
+ kfree(vfe_hw);
+free_vfe_hw_intf:
+ kfree(vfe_hw_intf);
+end:
+ return rc;
+}
+
+int cam_vfe_hw_init(struct cam_hw_intf **vfe_hw, uint32_t hw_idx)
+{
+ int rc = 0;
+
+ if (cam_vfe_hw_list[hw_idx]) {
+ *vfe_hw = cam_vfe_hw_list[hw_idx];
+ rc = 0;
+ } else {
+ *vfe_hw = NULL;
+ rc = -ENODEV;
+ }
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h
new file mode 100644
index 0000000..ca54d81
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_dev.h
@@ -0,0 +1,30 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_DEV_H_
+#define _CAM_VFE_DEV_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * cam_vfe_probe()
+ *
+ * @brief: Driver probe function called on Boot
+ *
+ * @pdev: Platform Device pointer
+ *
+ * @Return: 0: Success
+ * Non-zero: Failure
+ */
+int cam_vfe_probe(struct platform_device *pdev);
+
+#endif /* _CAM_VFE_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
new file mode 100644
index 0000000..3670ca9
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
@@ -0,0 +1,165 @@
+/* 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/slab.h>
+#include "cam_cpas_api.h"
+#include "cam_vfe_soc.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static int cam_vfe_get_dt_properties(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc) {
+ pr_err("Error! get DT properties failed\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static int cam_vfe_request_platform_resource(
+ struct cam_hw_soc_info *soc_info,
+ irq_handler_t vfe_irq_handler, void *irq_data)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_request_platform_resource(soc_info, vfe_irq_handler,
+ irq_data);
+
+ return rc;
+}
+
+int cam_vfe_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t vfe_irq_handler, void *irq_data)
+{
+ int rc = 0;
+ struct cam_vfe_soc_private *soc_private;
+ struct cam_cpas_register_params cpas_register_param;
+
+ soc_private = kzalloc(sizeof(struct cam_vfe_soc_private),
+ GFP_KERNEL);
+ if (!soc_private) {
+ CDBG("Error! soc_private Alloc Failed\n");
+ return -ENOMEM;
+ }
+ soc_info->soc_private = soc_private;
+
+ rc = cam_vfe_get_dt_properties(soc_info);
+ if (rc < 0) {
+ pr_err("Error! Get DT properties failed\n");
+ goto free_soc_private;
+ }
+
+ rc = cam_vfe_request_platform_resource(soc_info, vfe_irq_handler,
+ irq_data);
+ if (rc < 0) {
+ pr_err("Error! Request platform resources failed\n");
+ goto free_soc_private;
+ }
+
+ memset(&cpas_register_param, 0, sizeof(cpas_register_param));
+ strlcpy(cpas_register_param.identifier, "ife",
+ CAM_HW_IDENTIFIER_LENGTH);
+ cpas_register_param.cell_index = soc_info->index;
+ cpas_register_param.dev = &soc_info->pdev->dev;
+ rc = cam_cpas_register_client(&cpas_register_param);
+ if (rc) {
+ pr_err("CPAS registration failed\n");
+ goto release_soc;
+ } else {
+ soc_private->cpas_handle = cpas_register_param.client_handle;
+ }
+
+ return rc;
+
+release_soc:
+ cam_soc_util_release_platform_resource(soc_info);
+free_soc_private:
+ kfree(soc_private);
+
+ return rc;
+}
+
+int cam_vfe_enable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+ struct cam_vfe_soc_private *soc_private;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+
+ if (!soc_info) {
+ pr_err("Error! Invalid params\n");
+ rc = -EINVAL;
+ goto end;
+ }
+ soc_private = soc_info->soc_private;
+
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+
+ axi_vote.compressed_bw = 640000000;
+ axi_vote.uncompressed_bw = 640000000;
+
+ rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote);
+ if (rc) {
+ pr_err("Error! CPAS start failed.\n");
+ rc = -EFAULT;
+ goto end;
+ }
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, true);
+ if (rc) {
+ pr_err("Error! enable platform failed\n");
+ goto stop_cpas;
+ }
+
+ return rc;
+
+stop_cpas:
+ cam_cpas_stop(soc_private->cpas_handle);
+end:
+ return rc;
+}
+
+
+int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+ struct cam_vfe_soc_private *soc_private;
+
+ if (!soc_info) {
+ pr_err("Error! Invalid params\n");
+ rc = -EINVAL;
+ return rc;
+ }
+ soc_private = soc_info->soc_private;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+ if (rc) {
+ pr_err("%s: disable platform failed\n", __func__);
+ return rc;
+ }
+
+ rc = cam_cpas_stop(soc_private->cpas_handle);
+ if (rc) {
+ pr_err("Error! CPAS stop failed.\n");
+ return rc;
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h
new file mode 100644
index 0000000..27fb192
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h
@@ -0,0 +1,71 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_SOC_H_
+#define _CAM_VFE_SOC_H_
+
+#include "cam_soc_util.h"
+#include "cam_isp_hw.h"
+
+/*
+ * struct cam_vfe_soc_private:
+ *
+ * @Brief: Private SOC data specific to VFE HW Driver
+ *
+ * @cpas_handle: Handle returned on registering with CPAS driver.
+ * This handle is used for all further interface
+ * with CPAS.
+ */
+struct cam_vfe_soc_private {
+ uint32_t cpas_handle;
+};
+
+/*
+ * cam_vfe_init_soc_resources()
+ *
+ * @Brief: Initialize SOC resources including private data
+ *
+ * @soc_info: Device soc information
+ * @handler: Irq handler function pointer
+ * @irq_data: Irq handler function Callback data
+ *
+ * @Return: 0: Success
+ * Non-zero: Failure
+ */
+int cam_vfe_init_soc_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t vfe_irq_handler, void *irq_data);
+
+/*
+ * cam_vfe_enable_soc_resources()
+ *
+ * @brief: Enable regulator, irq resources, start CPAS
+ *
+ * @soc_info: Device soc information
+ *
+ * @Return: 0: Success
+ * Non-zero: Failure
+ */
+int cam_vfe_enable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+/*
+ * cam_vfe_disable_soc_resources()
+ *
+ * @brief: Disable regulator, irq resources, stop CPAS
+ *
+ * @soc_info: Device soc information
+ *
+ * @Return: 0: Success
+ * Non-zero: Failure
+ */
+int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info);
+
+#endif /* _CAM_VFE_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
new file mode 100644
index 0000000..77e4eb3
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c
new file mode 100644
index 0000000..2245ab1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.c
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+#include <linux/module.h>
+#include "cam_vfe170.h"
+#include "cam_vfe_hw_intf.h"
+#include "cam_vfe_core.h"
+#include "cam_vfe_dev.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+static const struct of_device_id cam_vfe170_dt_match[] = {
+ {
+ .compatible = "qcom,vfe170",
+ .data = &cam_vfe170_hw_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_vfe170_dt_match);
+
+static struct platform_driver cam_vfe170_driver = {
+ .probe = cam_vfe_probe,
+ .driver = {
+ .name = "cam_vfe170",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_vfe170_dt_match,
+ },
+};
+
+static int __init cam_vfe170_init_module(void)
+{
+ return platform_driver_register(&cam_vfe170_driver);
+}
+
+static void __exit cam_vfe170_exit_module(void)
+{
+ platform_driver_unregister(&cam_vfe170_driver);
+}
+
+module_init(cam_vfe170_init_module);
+module_exit(cam_vfe170_exit_module);
+MODULE_DESCRIPTION("CAM VFE170 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
new file mode 100644
index 0000000..b550071
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
@@ -0,0 +1,781 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE170_H_
+#define _CAM_VFE170_H_
+
+#include "cam_vfe_camif_ver2.h"
+#include "cam_vfe_bus_ver2.h"
+#include "cam_irq_controller.h"
+#include "cam_vfe_top_ver2.h"
+#include "cam_vfe_core.h"
+
+static struct cam_irq_register_set vfe170_top_irq_reg_set[2] = {
+ {
+ .mask_reg_offset = 0x0000005C,
+ .clear_reg_offset = 0x00000064,
+ .status_reg_offset = 0x0000006C,
+ },
+ {
+ .mask_reg_offset = 0x00000060,
+ .clear_reg_offset = 0x00000068,
+ .status_reg_offset = 0x00000070,
+ },
+};
+
+static struct cam_irq_controller_reg_info vfe170_top_irq_reg_info = {
+ .num_registers = 2,
+ .irq_reg_set = vfe170_top_irq_reg_set,
+ .global_clear_offset = 0x00000058,
+ .global_clear_bitmask = 0x00000001,
+};
+
+static struct cam_vfe_camif_ver2_reg vfe170_camif_reg = {
+ .camif_cmd = 0x00000478,
+ .camif_config = 0x0000047C,
+ .line_skip_pattern = 0x00000488,
+ .pixel_skip_pattern = 0x0000048C,
+ .skip_period = 0x00000490,
+ .irq_subsample_pattern = 0x0000049C,
+ .epoch_irq = 0x000004A0,
+ .raw_crop_width_cfg = 0x00000CE4,
+ .raw_crop_height_cfg = 0x00000CE8,
+};
+
+static struct cam_vfe_camif_reg_data vfe_170_camif_reg_data = {
+ .raw_crop_first_pixel_shift = 16,
+ .raw_crop_first_pixel_mask = 0xFFFF,
+ .raw_crop_last_pixel_shift = 0x0,
+ .raw_crop_last_pixel_mask = 0x3FFF,
+ .raw_crop_first_line_shift = 16,
+ .raw_crop_first_line_mask = 0xFFFF,
+ .raw_crop_last_line_shift = 0,
+ .raw_crop_last_line_mask = 0x3FFF,
+ .input_mux_sel_shift = 5,
+ .input_mux_sel_mask = 0x3,
+ .extern_reg_update_shift = 4,
+ .extern_reg_update_mask = 1,
+ .pixel_pattern_shift = 0,
+ .pixel_pattern_mask = 0x7,
+ .epoch_line_cfg = 0x140000,
+ .sof_irq_mask = 0x00000001,
+ .epoch0_irq_mask = 0x00000004,
+ .reg_update_irq_mask = 0x00000010,
+};
+
+struct cam_vfe_top_ver2_reg_offset_module_ctrl lens_170_reg = {
+ .reset = 0x0000001C,
+ .cgc_ovd = 0x0000002C,
+ .enable = 0x00000040,
+};
+
+struct cam_vfe_top_ver2_reg_offset_module_ctrl stats_170_reg = {
+ .reset = 0x00000020,
+ .cgc_ovd = 0x00000030,
+ .enable = 0x00000044,
+};
+
+struct cam_vfe_top_ver2_reg_offset_module_ctrl color_170_reg = {
+ .reset = 0x00000024,
+ .cgc_ovd = 0x00000034,
+ .enable = 0x00000048,
+};
+
+struct cam_vfe_top_ver2_reg_offset_module_ctrl zoom_170_reg = {
+ .reset = 0x00000028,
+ .cgc_ovd = 0x00000038,
+ .enable = 0x0000004C,
+};
+
+static struct cam_vfe_top_ver2_reg_offset_common vfe170_top_common_reg = {
+ .hw_version = 0x00000000,
+ .hw_capability = 0x00000004,
+ .lens_feature = 0x00000008,
+ .stats_feature = 0x0000000C,
+ .color_feature = 0x00000010,
+ .zoom_feature = 0x00000014,
+ .global_reset_cmd = 0x00000018,
+ .module_ctrl = {
+ &lens_170_reg,
+ &stats_170_reg,
+ &color_170_reg,
+ &zoom_170_reg,
+ },
+ .bus_cgc_ovd = 0x0000003C,
+ .core_cfg = 0x00000050,
+ .three_D_cfg = 0x00000054,
+ .violation_status = 0x0000007C,
+ .reg_update_cmd = 0x000004AC,
+};
+
+static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = {
+ .common_reg = &vfe170_top_common_reg,
+ .camif_hw_info = {
+ .common_reg = &vfe170_top_common_reg,
+ .camif_reg = &vfe170_camif_reg,
+ .reg_data = &vfe_170_camif_reg_data,
+ },
+ .mux_type = {
+ CAM_VFE_CAMIF_VER_2_0,
+ CAM_VFE_RDI_VER_1_0,
+ CAM_VFE_RDI_VER_1_0,
+ CAM_VFE_RDI_VER_1_0,
+ },
+};
+
+static struct cam_irq_register_set vfe170_bus_irq_reg[3] = {
+ {
+ .mask_reg_offset = 0x00002044,
+ .clear_reg_offset = 0x00002050,
+ .status_reg_offset = 0x0000205C,
+ },
+ {
+ .mask_reg_offset = 0x00002048,
+ .clear_reg_offset = 0x00002054,
+ .status_reg_offset = 0x00002060,
+ },
+ {
+ .mask_reg_offset = 0x0000204C,
+ .clear_reg_offset = 0x00002058,
+ .status_reg_offset = 0x00002064,
+ },
+};
+
+static struct cam_vfe_bus_ver2_reg_offset_ubwc_client ubwc_regs_client_3 = {
+ .tile_cfg = 0x0000252C,
+ .h_init = 0x00002530,
+ .v_init = 0x00002534,
+ .meta_addr = 0x00002538,
+ .meta_offset = 0x0000253C,
+ .meta_stride = 0x00002540,
+ .mode_cfg = 0x00002544,
+};
+
+static struct cam_vfe_bus_ver2_reg_offset_ubwc_client ubwc_regs_client_4 = {
+ .tile_cfg = 0x0000262C,
+ .h_init = 0x00002630,
+ .v_init = 0x00002634,
+ .meta_addr = 0x00002638,
+ .meta_offset = 0x0000263C,
+ .meta_stride = 0x00002640,
+ .mode_cfg = 0x00002644,
+};
+
+static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = {
+ .common_reg = {
+ .hw_version = 0x00002000,
+ .hw_capability = 0x00002004,
+ .sw_reset = 0x00002008,
+ .cgc_ovd = 0x0000200C,
+ .pwr_iso_cfg = 0x000020CC,
+ .dual_master_comp_cfg = 0x00002028,
+ .irq_reg_info = {
+ .num_registers = 3,
+ .irq_reg_set = vfe170_bus_irq_reg,
+ .global_clear_offset = 0x00002068,
+ .global_clear_bitmask = 0x00000001,
+ },
+ .comp_error_status = 0x0000206C,
+ .comp_ovrwr_status = 0x00002070,
+ .dual_comp_error_status = 0x00002074,
+ .dual_comp_error_status = 0x00002078,
+ },
+ .bus_client_reg = {
+ /* BUS Client 0 */
+ {
+ .status0 = 0x00002200,
+ .status1 = 0x00002204,
+ .cfg = 0x00002208,
+ .header_addr = 0x0000220C,
+ .header_cfg = 0x00002210,
+ .image_addr = 0x00002214,
+ .image_addr_offset = 0x00002218,
+ .buffer_width_cfg = 0x0000221C,
+ .buffer_height_cfg = 0x00002220,
+ .packer_cfg = 0x00002224,
+ .stride = 0x00002228,
+ .irq_subsample_period = 0x00002248,
+ .irq_subsample_pattern = 0x0000224C,
+ .framedrop_period = 0x00002250,
+ .framedrop_pattern = 0x00002254,
+ .frame_inc = 0x00002258,
+ .burst_limit = 0x0000225C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 1 */
+ {
+ .status0 = 0x00002300,
+ .status1 = 0x00002304,
+ .cfg = 0x00002308,
+ .header_addr = 0x0000230C,
+ .header_cfg = 0x00002310,
+ .image_addr = 0x00002314,
+ .image_addr_offset = 0x00002318,
+ .buffer_width_cfg = 0x0000231C,
+ .buffer_height_cfg = 0x00002320,
+ .packer_cfg = 0x00002324,
+ .stride = 0x00002328,
+ .irq_subsample_period = 0x00002348,
+ .irq_subsample_pattern = 0x0000234C,
+ .framedrop_period = 0x00002350,
+ .framedrop_pattern = 0x00002354,
+ .frame_inc = 0x00002358,
+ .burst_limit = 0x0000235C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 2 */
+ {
+ .status0 = 0x00002400,
+ .status1 = 0x00002404,
+ .cfg = 0x00002408,
+ .header_addr = 0x0000240C,
+ .header_cfg = 0x00002410,
+ .image_addr = 0x00002414,
+ .image_addr_offset = 0x00002418,
+ .buffer_width_cfg = 0x0000241C,
+ .buffer_height_cfg = 0x00002420,
+ .packer_cfg = 0x00002424,
+ .stride = 0x00002428,
+ .irq_subsample_period = 0x00002448,
+ .irq_subsample_pattern = 0x0000244C,
+ .framedrop_period = 0x00002450,
+ .framedrop_pattern = 0x00002454,
+ .frame_inc = 0x00002458,
+ .burst_limit = 0x0000245C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 3 */
+ {
+ .status0 = 0x00002500,
+ .status1 = 0x00002504,
+ .cfg = 0x00002508,
+ .header_addr = 0x0000250C,
+ .header_cfg = 0x00002510,
+ .image_addr = 0x00002514,
+ .image_addr_offset = 0x00002518,
+ .buffer_width_cfg = 0x0000251C,
+ .buffer_height_cfg = 0x00002520,
+ .packer_cfg = 0x00002524,
+ .stride = 0x00002528,
+ .irq_subsample_period = 0x00002548,
+ .irq_subsample_pattern = 0x0000254C,
+ .framedrop_period = 0x00002550,
+ .framedrop_pattern = 0x00002554,
+ .frame_inc = 0x00002558,
+ .burst_limit = 0x0000255C,
+ .ubwc_regs = &ubwc_regs_client_3,
+ },
+ /* BUS Client 4 */
+ {
+ .status0 = 0x00002600,
+ .status1 = 0x00002604,
+ .cfg = 0x00002608,
+ .header_addr = 0x0000260C,
+ .header_cfg = 0x00002610,
+ .image_addr = 0x00002614,
+ .image_addr_offset = 0x00002618,
+ .buffer_width_cfg = 0x0000261C,
+ .buffer_height_cfg = 0x00002620,
+ .packer_cfg = 0x00002624,
+ .stride = 0x00002628,
+ .irq_subsample_period = 0x00002648,
+ .irq_subsample_pattern = 0x0000264C,
+ .framedrop_period = 0x00002650,
+ .framedrop_pattern = 0x00002654,
+ .frame_inc = 0x00002658,
+ .burst_limit = 0x0000265C,
+ .ubwc_regs = &ubwc_regs_client_4,
+ },
+ /* BUS Client 5 */
+ {
+ .status0 = 0x00002700,
+ .status1 = 0x00002704,
+ .cfg = 0x00002708,
+ .header_addr = 0x0000270C,
+ .header_cfg = 0x00002710,
+ .image_addr = 0x00002714,
+ .image_addr_offset = 0x00002718,
+ .buffer_width_cfg = 0x0000271C,
+ .buffer_height_cfg = 0x00002720,
+ .packer_cfg = 0x00002724,
+ .stride = 0x00002728,
+ .irq_subsample_period = 0x00002748,
+ .irq_subsample_pattern = 0x0000274C,
+ .framedrop_period = 0x00002750,
+ .framedrop_pattern = 0x00002754,
+ .frame_inc = 0x00002758,
+ .burst_limit = 0x0000275C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 6 */
+ {
+ .status0 = 0x00002800,
+ .status1 = 0x00002804,
+ .cfg = 0x00002808,
+ .header_addr = 0x0000280C,
+ .header_cfg = 0x00002810,
+ .image_addr = 0x00002814,
+ .image_addr_offset = 0x00002818,
+ .buffer_width_cfg = 0x0000281C,
+ .buffer_height_cfg = 0x00002820,
+ .packer_cfg = 0x00002824,
+ .stride = 0x00002828,
+ .irq_subsample_period = 0x00002848,
+ .irq_subsample_pattern = 0x0000284C,
+ .framedrop_period = 0x00002850,
+ .framedrop_pattern = 0x00002854,
+ .frame_inc = 0x00002858,
+ .burst_limit = 0x0000285C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 7 */
+ {
+ .status0 = 0x00002900,
+ .status1 = 0x00002904,
+ .cfg = 0x00002908,
+ .header_addr = 0x0000290C,
+ .header_cfg = 0x00002910,
+ .image_addr = 0x00002914,
+ .image_addr_offset = 0x00002918,
+ .buffer_width_cfg = 0x0000291C,
+ .buffer_height_cfg = 0x00002920,
+ .packer_cfg = 0x00002924,
+ .stride = 0x00002928,
+ .irq_subsample_period = 0x00002948,
+ .irq_subsample_pattern = 0x0000294C,
+ .framedrop_period = 0x00002950,
+ .framedrop_pattern = 0x00002954,
+ .frame_inc = 0x00002958,
+ .burst_limit = 0x0000295C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 8 */
+ {
+ .status0 = 0x00002A00,
+ .status1 = 0x00002A04,
+ .cfg = 0x00002A08,
+ .header_addr = 0x00002A0C,
+ .header_cfg = 0x00002A10,
+ .image_addr = 0x00002A14,
+ .image_addr_offset = 0x00002A18,
+ .buffer_width_cfg = 0x00002A1C,
+ .buffer_height_cfg = 0x00002A20,
+ .packer_cfg = 0x00002A24,
+ .stride = 0x00002A28,
+ .irq_subsample_period = 0x00002A48,
+ .irq_subsample_pattern = 0x00002A4C,
+ .framedrop_period = 0x00002A50,
+ .framedrop_pattern = 0x00002A54,
+ .frame_inc = 0x00002A58,
+ .burst_limit = 0x00002A5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 9 */
+ {
+ .status0 = 0x00002B00,
+ .status1 = 0x00002B04,
+ .cfg = 0x00002B08,
+ .header_addr = 0x00002B0C,
+ .header_cfg = 0x00002B10,
+ .image_addr = 0x00002B14,
+ .image_addr_offset = 0x00002B18,
+ .buffer_width_cfg = 0x00002B1C,
+ .buffer_height_cfg = 0x00002B20,
+ .packer_cfg = 0x00002B24,
+ .stride = 0x00002B28,
+ .irq_subsample_period = 0x00002B48,
+ .irq_subsample_pattern = 0x00002B4C,
+ .framedrop_period = 0x00002B50,
+ .framedrop_pattern = 0x00002B54,
+ .frame_inc = 0x00002B58,
+ .burst_limit = 0x00002B5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 10 */
+ {
+ .status0 = 0x00002C00,
+ .status1 = 0x00002C04,
+ .cfg = 0x00002C08,
+ .header_addr = 0x00002C0C,
+ .header_cfg = 0x00002C10,
+ .image_addr = 0x00002C14,
+ .image_addr_offset = 0x00002C18,
+ .buffer_width_cfg = 0x00002C1C,
+ .buffer_height_cfg = 0x00002C20,
+ .packer_cfg = 0x00002C24,
+ .stride = 0x00002C28,
+ .irq_subsample_period = 0x00002C48,
+ .irq_subsample_pattern = 0x00002C4C,
+ .framedrop_period = 0x00002C50,
+ .framedrop_pattern = 0x00002C54,
+ .frame_inc = 0x00002C58,
+ .burst_limit = 0x00002C5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 11 */
+ {
+ .status0 = 0x00002D00,
+ .status1 = 0x00002D04,
+ .cfg = 0x00002D08,
+ .header_addr = 0x00002D0C,
+ .header_cfg = 0x00002D10,
+ .image_addr = 0x00002D14,
+ .image_addr_offset = 0x00002D18,
+ .buffer_width_cfg = 0x00002D1C,
+ .buffer_height_cfg = 0x00002D20,
+ .packer_cfg = 0x00002D24,
+ .stride = 0x00002D28,
+ .irq_subsample_period = 0x00002D48,
+ .irq_subsample_pattern = 0x00002D4C,
+ .framedrop_period = 0x00002D50,
+ .framedrop_pattern = 0x00002D54,
+ .frame_inc = 0x00002D58,
+ .burst_limit = 0x00002D5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 12 */
+ {
+ .status0 = 0x00002E00,
+ .status1 = 0x00002E04,
+ .cfg = 0x00002E08,
+ .header_addr = 0x00002E0C,
+ .header_cfg = 0x00002E10,
+ .image_addr = 0x00002E14,
+ .image_addr_offset = 0x00002E18,
+ .buffer_width_cfg = 0x00002E1C,
+ .buffer_height_cfg = 0x00002E20,
+ .packer_cfg = 0x00002E24,
+ .stride = 0x00002E28,
+ .irq_subsample_period = 0x00002E48,
+ .irq_subsample_pattern = 0x00002E4C,
+ .framedrop_period = 0x00002E50,
+ .framedrop_pattern = 0x00002E54,
+ .frame_inc = 0x00002E58,
+ .burst_limit = 0x00002E5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 13 */
+ {
+ .status0 = 0x00002F00,
+ .status1 = 0x00002F04,
+ .cfg = 0x00002F08,
+ .header_addr = 0x00002F0C,
+ .header_cfg = 0x00002F10,
+ .image_addr = 0x00002F14,
+ .image_addr_offset = 0x00002F18,
+ .buffer_width_cfg = 0x00002F1C,
+ .buffer_height_cfg = 0x00002F20,
+ .packer_cfg = 0x00002F24,
+ .stride = 0x00002F28,
+ .irq_subsample_period = 0x00002F48,
+ .irq_subsample_pattern = 0x00002F4C,
+ .framedrop_period = 0x00002F50,
+ .framedrop_pattern = 0x00002F54,
+ .frame_inc = 0x00002F58,
+ .burst_limit = 0x00002F5C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 14 */
+ {
+ .status0 = 0x00003000,
+ .status1 = 0x00003004,
+ .cfg = 0x00003008,
+ .header_addr = 0x0000300C,
+ .header_cfg = 0x00003010,
+ .image_addr = 0x00003014,
+ .image_addr_offset = 0x00003018,
+ .buffer_width_cfg = 0x0000301C,
+ .buffer_height_cfg = 0x00003020,
+ .packer_cfg = 0x00003024,
+ .stride = 0x00003028,
+ .irq_subsample_period = 0x00003048,
+ .irq_subsample_pattern = 0x0000304C,
+ .framedrop_period = 0x00003050,
+ .framedrop_pattern = 0x00003054,
+ .frame_inc = 0x00003058,
+ .burst_limit = 0x0000305C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 15 */
+ {
+ .status0 = 0x00003100,
+ .status1 = 0x00003104,
+ .cfg = 0x00003108,
+ .header_addr = 0x0000310C,
+ .header_cfg = 0x00003110,
+ .image_addr = 0x00003114,
+ .image_addr_offset = 0x00003118,
+ .buffer_width_cfg = 0x0000311C,
+ .buffer_height_cfg = 0x00003120,
+ .packer_cfg = 0x00003124,
+ .stride = 0x00003128,
+ .irq_subsample_period = 0x00003148,
+ .irq_subsample_pattern = 0x0000314C,
+ .framedrop_period = 0x00003150,
+ .framedrop_pattern = 0x00003154,
+ .frame_inc = 0x00003158,
+ .burst_limit = 0x0000315C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 16 */
+ {
+ .status0 = 0x00003200,
+ .status1 = 0x00003204,
+ .cfg = 0x00003208,
+ .header_addr = 0x0000320C,
+ .header_cfg = 0x00003210,
+ .image_addr = 0x00003214,
+ .image_addr_offset = 0x00003218,
+ .buffer_width_cfg = 0x0000321C,
+ .buffer_height_cfg = 0x00003220,
+ .packer_cfg = 0x00003224,
+ .stride = 0x00003228,
+ .irq_subsample_period = 0x00003248,
+ .irq_subsample_pattern = 0x0000324C,
+ .framedrop_period = 0x00003250,
+ .framedrop_pattern = 0x00003254,
+ .frame_inc = 0x00003258,
+ .burst_limit = 0x0000325C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 17 */
+ {
+ .status0 = 0x00003300,
+ .status1 = 0x00003304,
+ .cfg = 0x00003308,
+ .header_addr = 0x0000330C,
+ .header_cfg = 0x00003310,
+ .image_addr = 0x00003314,
+ .image_addr_offset = 0x00003318,
+ .buffer_width_cfg = 0x0000331C,
+ .buffer_height_cfg = 0x00003320,
+ .packer_cfg = 0x00003324,
+ .stride = 0x00003328,
+ .irq_subsample_period = 0x00003348,
+ .irq_subsample_pattern = 0x0000334C,
+ .framedrop_period = 0x00003350,
+ .framedrop_pattern = 0x00003354,
+ .frame_inc = 0x00003358,
+ .burst_limit = 0x0000335C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 18 */
+ {
+ .status0 = 0x00003400,
+ .status1 = 0x00003404,
+ .cfg = 0x00003408,
+ .header_addr = 0x0000340C,
+ .header_cfg = 0x00003410,
+ .image_addr = 0x00003414,
+ .image_addr_offset = 0x00003418,
+ .buffer_width_cfg = 0x0000341C,
+ .buffer_height_cfg = 0x00003420,
+ .packer_cfg = 0x00003424,
+ .stride = 0x00003428,
+ .irq_subsample_period = 0x00003448,
+ .irq_subsample_pattern = 0x0000344C,
+ .framedrop_period = 0x00003450,
+ .framedrop_pattern = 0x00003454,
+ .frame_inc = 0x00003458,
+ .burst_limit = 0x0000345C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 19 */
+ {
+ .status0 = 0x00003500,
+ .status1 = 0x00003504,
+ .cfg = 0x00003508,
+ .header_addr = 0x0000350C,
+ .header_cfg = 0x00003510,
+ .image_addr = 0x00003514,
+ .image_addr_offset = 0x00003518,
+ .buffer_width_cfg = 0x0000351C,
+ .buffer_height_cfg = 0x00003520,
+ .packer_cfg = 0x00003524,
+ .stride = 0x00003528,
+ .irq_subsample_period = 0x00003548,
+ .irq_subsample_pattern = 0x0000354C,
+ .framedrop_period = 0x00003550,
+ .framedrop_pattern = 0x00003554,
+ .frame_inc = 0x00003558,
+ .burst_limit = 0x0000355C,
+ .ubwc_regs = NULL,
+ },
+ },
+ .comp_grp_reg = {
+ /* CAM_VFE_BUS_VER2_COMP_GRP_0 */
+ {
+ .comp_mask = 0x00002010,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_1 */
+ {
+ .comp_mask = 0x00002014,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_2 */
+ {
+ .comp_mask = 0x00002018,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_3 */
+ {
+ .comp_mask = 0x0000201C,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_4 */
+ {
+ .comp_mask = 0x00002020,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_5 */
+ {
+ .comp_mask = 0x00002024,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */
+ {
+ .comp_mask = 0x0000202C,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */
+ {
+ .comp_mask = 0x00002030,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */
+ {
+ .comp_mask = 0x00002034,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */
+ {
+ .comp_mask = 0x00002038,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */
+ {
+ .comp_mask = 0x0000203C,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */
+ {
+ .comp_mask = 0x00002040,
+ },
+ },
+ .vfe_out_hw_info = {
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_FULL,
+ .max_width = 4096,
+ .max_height = 4096,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_DS4,
+ .max_width = 1920,
+ .max_height = 1080,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_DS16,
+ .max_width = 1920,
+ .max_height = 1080,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_FD,
+ .max_width = 1920,
+ .max_height = 1080,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_PDAF,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST,
+ .max_width = 1920,
+ .max_height = 1080,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type =
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ },
+};
+
+struct cam_vfe_hw_info cam_vfe170_hw_info = {
+ .irq_reg_info = &vfe170_top_irq_reg_info,
+
+ .bus_version = CAM_VFE_BUS_VER_2_0,
+ .bus_hw_info = &vfe170_bus_hw_info,
+
+ .top_version = CAM_VFE_TOP_VER_2_0,
+ .top_hw_info = &vfe170_top_hw_info,
+
+ .camif_version = CAM_VFE_CAMIF_VER_2_0,
+ .camif_reg = &vfe170_camif_reg,
+
+};
+
+#endif /* _CAM_VFE170_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
new file mode 100644
index 0000000..cea1137
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
@@ -0,0 +1,10 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_bus.o cam_vfe_bus_ver2.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c
new file mode 100644
index 0000000..50952f8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus.c
@@ -0,0 +1,39 @@
+/* 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 "cam_vfe_bus.h"
+#include "cam_vfe_bus_ver1.h"
+#include "cam_vfe_bus_ver2.h"
+
+int cam_vfe_bus_init(uint32_t bus_version,
+ 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 rc = -ENODEV;
+
+ switch (bus_version) {
+ case CAM_VFE_BUS_VER_2_0:
+ rc = cam_vfe_bus_ver2_init(mem_base, hw_intf, bus_hw_info,
+ vfe_irq_controller, vfe_bus);
+ break;
+ default:
+ pr_err("Unsupported Bus Version %x\n", bus_version);
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h
new file mode 100644
index 0000000..3572451
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver1.h
@@ -0,0 +1,120 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_BUS_VER1_H_
+#define _CAM_VFE_BUS_VER1_H_
+
+enum cam_vfe_bus_ver1_pingpong_id {
+ CAM_VFE_BUS_VER1_PING,
+ CAM_VFE_BUS_VER1_PONG,
+ CAM_VFE_BUS_VER1_PINGPONG_MAX,
+};
+
+enum cam_vfe_bus_ver1_wm_type {
+ CAM_VFE_BUS_WM_TYPE_IMAGE,
+ CAM_VFE_BUS_WM_TYPE_STATS,
+ CAM_VFE_BUS_WM_TYPE_MAX,
+};
+
+enum cam_vfe_bus_ver1_comp_grp_type {
+ CAM_VFE_BUS_VER1_COMP_GRP_IMG0,
+ CAM_VFE_BUS_VER1_COMP_GRP_IMG1,
+ CAM_VFE_BUS_VER1_COMP_GRP_IMG2,
+ CAM_VFE_BUS_VER1_COMP_GRP_IMG3,
+ CAM_VFE_BUS_VER1_COMP_GRP_STATS0,
+ CAM_VFE_BUS_VER1_COMP_GRP_STATS1,
+ CAM_VFE_BUS_VER1_COMP_GRP_MAX,
+};
+
+struct cam_vfe_bus_ver1_common_reg {
+ uint32_t cmd_offset;
+ uint32_t cfg_offset;
+ uint32_t io_fmt_offset;
+ uint32_t argb_cfg_offset;
+ uint32_t xbar_cfg0_offset;
+ uint32_t xbar_cfg1_offset;
+ uint32_t xbar_cfg2_offset;
+ uint32_t xbar_cfg3_offset;
+ uint32_t ping_pong_status_reg;
+};
+
+struct cam_vfe_bus_ver1_wm_reg {
+ uint32_t wm_cfg_offset;
+ uint32_t ping_addr_offset;
+ uint32_t ping_max_addr_offset;
+ uint32_t pong_addr_offset;
+ uint32_t pong_max_addr_offset;
+ uint32_t addr_cfg_offset;
+ uint32_t ub_cfg_offset;
+ uint32_t image_size_offset;
+ uint32_t buffer_cfg_offset;
+ uint32_t framedrop_pattern_offset;
+ uint32_t irq_subsample_pattern_offset;
+ uint32_t ping_pong_status_bit; /* 0 - 31 */
+ uint32_t composite_bit; /* 0 -31 */
+};
+
+struct cam_vfe_bus_ver1_wm_resource_data {
+ uint32_t index;
+ uint32_t wm_type;
+ uint32_t res_type;
+
+ uint32_t offset;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t scanline;
+
+ uint32_t burst_len;
+
+ uint32_t framedrop_period;
+ uint32_t framedrop_pattern;
+
+ uint32_t buf_valid[CAM_VFE_BUS_VER1_PINGPONG_MAX];
+ uint32_t ub_size;
+ uint32_t ub_offset;
+
+ struct cam_vfe_bus_ver1_wm_reg hw_regs;
+};
+
+struct cam_vfe_bus_ver1_comp_grp_reg {
+ enum cam_vfe_bus_ver1_comp_grp_type comp_grp_type;
+ uint32_t comp_grp_offset;
+};
+
+struct cam_vfe_bus_ver1_comp_grp {
+ struct cam_vfe_bus_ver1_comp_grp_reg reg_info;
+ struct list_head wm_list;
+ uint32_t cur_bit_mask;
+};
+
+/*
+ * cam_vfe_bus_ver1_init()
+ *
+ * @Brief: Initialize Bus layer
+ *
+ * @mem_base: Mapped base address of register space
+ * @hw_intf: HW Interface of HW to which this resource belongs
+ * @bus_hw_info: BUS HW info that contains details of BUS registers
+ * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top
+ * level IRQs
+ * @vfe_bus: Pointer to vfe_bus structure which will be filled
+ * and returned on successful initialize
+ */
+int cam_vfe_bus_ver1_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);
+
+#endif /* _CAM_VFE_BUS_VER1_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
new file mode 100644
index 0000000..48b0363
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -0,0 +1,1718 @@
+/* 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;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
new file mode 100644
index 0000000..e451174
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
@@ -0,0 +1,188 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_BUS_VER2_H_
+#define _CAM_VFE_BUS_VER2_H_
+
+#include "cam_irq_controller.h"
+#include "cam_vfe_bus.h"
+
+#define CAM_VFE_BUS_VER2_MAX_CLIENTS 20
+
+enum cam_vfe_bus_ver2_vfe_core_id {
+ CAM_VFE_BUS_VER2_VFE_CORE_0,
+ CAM_VFE_BUS_VER2_VFE_CORE_1,
+ CAM_VFE_BUS_VER2_VFE_CORE_2,
+ CAM_VFE_BUS_VER2_VFE_CORE_MAX,
+};
+
+enum cam_vfe_bus_ver2_comp_grp_type {
+ CAM_VFE_BUS_VER2_COMP_GRP_0,
+ CAM_VFE_BUS_VER2_COMP_GRP_1,
+ CAM_VFE_BUS_VER2_COMP_GRP_2,
+ CAM_VFE_BUS_VER2_COMP_GRP_3,
+ CAM_VFE_BUS_VER2_COMP_GRP_4,
+ CAM_VFE_BUS_VER2_COMP_GRP_5,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4,
+ CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5,
+ CAM_VFE_BUS_VER2_COMP_GRP_MAX,
+};
+
+enum cam_vfe_bus_ver2_vfe_out_type {
+ CAM_VFE_BUS_VER2_VFE_OUT_FULL,
+ CAM_VFE_BUS_VER2_VFE_OUT_DS4,
+ CAM_VFE_BUS_VER2_VFE_OUT_DS16,
+ CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP,
+ CAM_VFE_BUS_VER2_VFE_OUT_FD,
+ CAM_VFE_BUS_VER2_VFE_OUT_PDAF,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS,
+ CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST,
+ CAM_VFE_BUS_VER2_VFE_OUT_MAX,
+};
+
+/*
+ * struct cam_vfe_bus_ver2_reg_offset_common:
+ *
+ * @Brief: Common registers across all BUS Clients
+ */
+struct cam_vfe_bus_ver2_reg_offset_common {
+ uint32_t hw_version;
+ uint32_t hw_capability;
+ uint32_t sw_reset;
+ uint32_t cgc_ovd;
+ uint32_t pwr_iso_cfg;
+ uint32_t dual_master_comp_cfg;
+ struct cam_irq_controller_reg_info irq_reg_info;
+ uint32_t comp_error_status;
+ uint32_t comp_ovrwr_status;
+ uint32_t dual_comp_error_status;
+ uint32_t dual_comp_ovrwr_status;
+ uint32_t addr_sync_cfg;
+ uint32_t addr_syn_frame_hdr;
+ uint32_t addr_syn_no_sync;
+};
+
+/*
+ * struct cam_vfe_bus_ver2_reg_offset_ubwc_client:
+ *
+ * @Brief: UBWC register offsets for BUS Clients
+ */
+struct cam_vfe_bus_ver2_reg_offset_ubwc_client {
+ uint32_t tile_cfg;
+ uint32_t h_init;
+ uint32_t v_init;
+ uint32_t meta_addr;
+ uint32_t meta_offset;
+ uint32_t meta_stride;
+ uint32_t mode_cfg;
+};
+
+/*
+ * struct cam_vfe_bus_ver2_reg_offset_bus_client:
+ *
+ * @Brief: Register offsets for BUS Clients
+ */
+struct cam_vfe_bus_ver2_reg_offset_bus_client {
+ uint32_t status0;
+ uint32_t status1;
+ uint32_t cfg;
+ uint32_t header_addr;
+ uint32_t header_cfg;
+ uint32_t image_addr;
+ uint32_t image_addr_offset;
+ uint32_t buffer_width_cfg;
+ uint32_t buffer_height_cfg;
+ uint32_t packer_cfg;
+ uint32_t stride;
+ uint32_t irq_subsample_period;
+ uint32_t irq_subsample_pattern;
+ uint32_t framedrop_period;
+ uint32_t framedrop_pattern;
+ uint32_t frame_inc;
+ uint32_t burst_limit;
+ struct cam_vfe_bus_ver2_reg_offset_ubwc_client *ubwc_regs;
+};
+
+/*
+ * struct cam_vfe_bus_ver2_reg_offset_comp_grp:
+ *
+ * @Brief: Register offsets for Composite Group registers
+ */
+struct cam_vfe_bus_ver2_reg_offset_comp_grp {
+ uint32_t comp_mask;
+};
+
+/*
+ * struct cam_vfe_bus_ver2_vfe_out_hw_info:
+ *
+ * @Brief: HW capability of VFE Bus Client
+ */
+struct cam_vfe_bus_ver2_vfe_out_hw_info {
+ enum cam_vfe_bus_ver2_vfe_out_type vfe_out_type;
+ uint32_t max_width;
+ uint32_t max_height;
+};
+
+/*
+ * struct cam_vfe_bus_ver2_hw_info:
+ *
+ * @Brief: HW register info for entire Bus
+ *
+ * @common_reg: Common register details
+ * @bus_client_reg: Bus client register info
+ * @comp_reg_grp: Composite group register info
+ * @vfe_out_hw_info: VFE output capability
+ */
+struct cam_vfe_bus_ver2_hw_info {
+ struct cam_vfe_bus_ver2_reg_offset_common common_reg;
+ struct cam_vfe_bus_ver2_reg_offset_bus_client
+ bus_client_reg[CAM_VFE_BUS_VER2_MAX_CLIENTS];
+ struct cam_vfe_bus_ver2_reg_offset_comp_grp
+ comp_grp_reg[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
+ struct cam_vfe_bus_ver2_vfe_out_hw_info
+ vfe_out_hw_info[CAM_VFE_BUS_VER2_VFE_OUT_MAX];
+};
+
+/*
+ * cam_vfe_bus_ver2_init()
+ *
+ * @Brief: Initialize Bus layer
+ *
+ * @mem_base: Mapped base address of register space
+ * @hw_intf: HW Interface of HW to which this resource belongs
+ * @bus_hw_info: BUS HW info that contains details of BUS registers
+ * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top
+ * level IRQs
+ * @vfe_bus: Pointer to vfe_bus structure which will be filled
+ * and returned on successful initialize
+ */
+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);
+
+#endif /* _CAM_VFE_BUS_VER2_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
new file mode 100644
index 0000000..d202c13
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
@@ -0,0 +1,78 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_BUS_H_
+#define _CAM_VFE_BUS_H_
+
+#include <uapi/media/cam_isp.h>
+#include "cam_isp_hw.h"
+
+#define CAM_VFE_BUS_VER_1_0 0x1000
+#define CAM_VFE_BUS_VER_2_0 0x2000
+
+enum cam_vfe_bus_plane_type {
+ PLANE_Y,
+ PLANE_C,
+ PLANE_MAX,
+};
+
+/*
+ * struct cam_vfe_bus:
+ *
+ * @Brief: Bus interface structure
+ *
+ * @bus_priv: Private data of BUS
+ * @acquire_resource: Function pointer for acquiring BUS output resource
+ * @release_resource: Function pointer for releasing BUS resource
+ * @start_resource: Function for starting BUS Output resource
+ * @stop_resource: Function for stopping BUS Output resource
+ * @process_cmd: Function to process commands specific to BUS
+ * resources
+ * @top_half_handler: Top Half handler function
+ * @bottom_half_handler: Bottom Half handler function
+ */
+struct cam_vfe_bus {
+ void *bus_priv;
+
+ int (*acquire_resource)(void *bus_priv, void *acquire_args);
+ int (*release_resource)(void *bus_priv,
+ struct cam_isp_resource_node *vfe_out);
+ int (*start_resource)(struct cam_isp_resource_node *vfe_out);
+ int (*stop_resource)(struct cam_isp_resource_node *vfe_out);
+ int (*process_cmd)(void *priv, uint32_t cmd_type, void *cmd_args,
+ uint32_t arg_size);
+ CAM_IRQ_HANDLER_TOP_HALF top_half_handler;
+ CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler;
+};
+
+/*
+ * cam_vfe_bus_init()
+ *
+ * @Brief: Initialize Bus layer
+ *
+ * @bus_version: Version of BUS to initialize
+ * @mem_base: Mapped base address of register space
+ * @hw_intf: HW Interface of HW to which this resource belongs
+ * @bus_hw_info: BUS HW info that contains details of BUS registers
+ * @vfe_irq_controller: VFE IRQ Controller to use for subscribing to Top
+ * level IRQs
+ * @vfe_bus: Pointer to vfe_bus structure which will be filled
+ * and returned on successful initialize
+ */
+int cam_vfe_bus_init(uint32_t bus_version,
+ void __iomem *mem_base,
+ struct cam_hw_intf *hw_intf,
+ void *bus_hw_info,
+ void *vfe_irq_controller,
+ struct cam_vfe_bus **vfe_bus);
+
+#endif /* _CAM_VFE_BUS_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile
new file mode 100644
index 0000000..56dc513
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile
@@ -0,0 +1,10 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_top.o cam_vfe_top_ver2.o cam_vfe_camif_ver2.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
new file mode 100644
index 0000000..ff56115
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -0,0 +1,257 @@
+/* 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/slab.h>
+#include <uapi/media/cam_isp.h>
+#include "cam_io_util.h"
+#include "cam_isp_hw_mgr_intf.h"
+#include "cam_vfe_hw_intf.h"
+#include "cam_vfe_top.h"
+#include "cam_vfe_top_ver2.h"
+#include "cam_vfe_camif_ver2.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+struct cam_vfe_mux_camif_data {
+ void __iomem *mem_base;
+ struct cam_hw_intf *hw_intf;
+ struct cam_vfe_camif_ver2_reg *camif_reg;
+ struct cam_vfe_top_ver2_reg_offset_common *common_reg;
+ struct cam_vfe_camif_reg_data *reg_data;
+
+ enum cam_isp_hw_sync_mode sync_mode;
+ uint32_t pix_pattern;
+ uint32_t first_pixel;
+ uint32_t first_line;
+ uint32_t last_pixel;
+ uint32_t last_line;
+};
+
+static int cam_vfe_camif_validate_pix_pattern(uint32_t pattern)
+{
+ int rc;
+
+ switch (pattern) {
+ case CAM_ISP_PATTERN_BAYER_RGRGRG:
+ case CAM_ISP_PATTERN_BAYER_GRGRGR:
+ case CAM_ISP_PATTERN_BAYER_BGBGBG:
+ case CAM_ISP_PATTERN_BAYER_GBGBGB:
+ case CAM_ISP_PATTERN_YUV_YCBYCR:
+ case CAM_ISP_PATTERN_YUV_YCRYCB:
+ case CAM_ISP_PATTERN_YUV_CBYCRY:
+ case CAM_ISP_PATTERN_YUV_CRYCBY:
+ rc = 0;
+ break;
+ default:
+ pr_err("Error! Invalid pix pattern:%d\n", pattern);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+int cam_vfe_camif_ver2_acquire_resource(
+ struct cam_isp_resource_node *camif_res,
+ void *acquire_param)
+{
+ struct cam_vfe_mux_camif_data *camif_data;
+ struct cam_vfe_acquire_args *acquire_data;
+
+ int rc = 0;
+
+ camif_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+ acquire_data = (struct cam_vfe_acquire_args *)acquire_param;
+
+ rc = cam_vfe_camif_validate_pix_pattern(
+ acquire_data->vfe_in.in_port->test_pattern);
+ if (rc)
+ return rc;
+
+ camif_data->sync_mode = acquire_data->vfe_in.sync_mode;
+ camif_data->pix_pattern = acquire_data->vfe_in.in_port->test_pattern;
+ camif_data->first_pixel = acquire_data->vfe_in.in_port->left_start;
+ camif_data->last_pixel = acquire_data->vfe_in.in_port->left_stop;
+ camif_data->first_line = acquire_data->vfe_in.in_port->line_start;
+ camif_data->last_line = acquire_data->vfe_in.in_port->line_stop;
+
+ return rc;
+}
+
+static int cam_vfe_camif_resource_start(
+ struct cam_isp_resource_node *camif_res)
+{
+ struct cam_vfe_mux_camif_data *rsrc_data;
+ uint32_t val = 0;
+
+ if (!camif_res) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ if (camif_res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
+ pr_err("Error! Invalid camif res res_state:%d\n",
+ camif_res->res_state);
+ return -EINVAL;
+ }
+
+ rsrc_data = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+
+ /*config vfe core*/
+ val = (rsrc_data->pix_pattern <<
+ rsrc_data->reg_data->pixel_pattern_shift);
+ cam_io_w_mb(val, rsrc_data->mem_base + rsrc_data->common_reg->core_cfg);
+
+ cam_io_w_mb(0x00400040, rsrc_data->mem_base +
+ rsrc_data->camif_reg->camif_config);
+ cam_io_w_mb(0x1, rsrc_data->mem_base +
+ rsrc_data->camif_reg->line_skip_pattern);
+ cam_io_w_mb(0x1, rsrc_data->mem_base +
+ rsrc_data->camif_reg->pixel_skip_pattern);
+ cam_io_w_mb(0x0, rsrc_data->mem_base +
+ rsrc_data->camif_reg->skip_period);
+ cam_io_w_mb(0x1, rsrc_data->mem_base +
+ rsrc_data->camif_reg->irq_subsample_pattern);
+
+ /* epoch config with 20 line */
+ cam_io_w_mb(0x00140014,
+ rsrc_data->mem_base + rsrc_data->camif_reg->epoch_irq);
+
+ camif_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
+
+ /* Reg Update */
+ cam_io_w_mb(0x1, rsrc_data->mem_base + 0x4AC);
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+
+static int cam_vfe_camif_resource_stop(
+ struct cam_isp_resource_node *camif_res)
+{
+ struct cam_vfe_mux_camif_data *camif_priv;
+ struct cam_vfe_camif_ver2_reg *camif_reg;
+ int rc = 0;
+
+ if (!camif_res) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ if (camif_res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED ||
+ camif_res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE)
+ return 0;
+
+ camif_priv = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+ camif_reg = camif_priv->camif_reg;
+
+ if (camif_res->res_state == CAM_ISP_RESOURCE_STATE_STREAMING)
+ camif_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
+
+ return rc;
+}
+
+int cam_vfe_camif_process_cmd(void *priv,
+ uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
+{
+ return -EPERM;
+}
+
+static int cam_vfe_camif_handle_irq_top_half(uint32_t evt_id,
+ struct cam_irq_th_payload *th_payload)
+{
+ return -EPERM;
+}
+
+static int cam_vfe_camif_handle_irq_bottom_half(void *handler_priv,
+ void *evt_payload_priv)
+{
+ int ret = CAM_VFE_IRQ_STATUS_ERR;
+ struct cam_isp_resource_node *camif_node;
+ struct cam_vfe_mux_camif_data *camif_priv;
+ struct cam_vfe_top_irq_evt_payload *payload;
+ uint32_t irq_status0;
+
+ if (!handler_priv || !evt_payload_priv)
+ return ret;
+
+ camif_node = handler_priv;
+ camif_priv = camif_node->res_priv;
+ payload = evt_payload_priv;
+ irq_status0 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS0];
+
+ CDBG("event ID:%d\n", payload->evt_id);
+ CDBG("irq_status_0 = %x\n", irq_status0);
+
+ switch (payload->evt_id) {
+ case CAM_ISP_HW_EVENT_SOF:
+ if (irq_status0 & camif_priv->reg_data->sof_irq_mask) {
+ CDBG("Received SOF\n");
+ ret = CAM_VFE_IRQ_STATUS_SUCCESS;
+ }
+ break;
+ case CAM_ISP_HW_EVENT_EPOCH:
+ if (irq_status0 & camif_priv->reg_data->epoch0_irq_mask) {
+ CDBG("Received EPOCH\n");
+ ret = CAM_VFE_IRQ_STATUS_SUCCESS;
+ }
+ cam_vfe_put_evt_payload(payload->core_info, &payload);
+ break;
+ case CAM_ISP_HW_EVENT_REG_UPDATE:
+ if (irq_status0 & camif_priv->reg_data->reg_update_irq_mask) {
+ CDBG("Received REG_UPDATE_ACK\n");
+ ret = CAM_VFE_IRQ_STATUS_SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ CDBG("returing status = %d\n", ret);
+ return ret;
+}
+
+int cam_vfe_camif_ver2_init(
+ struct cam_hw_intf *hw_intf,
+ struct cam_hw_soc_info *soc_info,
+ void *camif_hw_info,
+ struct cam_isp_resource_node *camif_node)
+{
+ struct cam_vfe_mux_camif_data *camif_priv = NULL;
+ struct cam_vfe_camif_ver2_hw_info *camif_info = camif_hw_info;
+
+ camif_priv = kzalloc(sizeof(struct cam_vfe_mux_camif_data),
+ GFP_KERNEL);
+ if (!camif_priv) {
+ CDBG("Error! Failed to alloc for camif_priv\n");
+ return -ENOMEM;
+ }
+
+ camif_node->res_priv = camif_priv;
+
+ camif_priv->mem_base = soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base;
+ camif_priv->camif_reg = camif_info->camif_reg;
+ camif_priv->common_reg = camif_info->common_reg;
+ camif_priv->reg_data = camif_info->reg_data;
+ camif_priv->hw_intf = hw_intf;
+
+ camif_node->start = cam_vfe_camif_resource_start;
+ camif_node->stop = cam_vfe_camif_resource_stop;
+ camif_node->top_half_handler = cam_vfe_camif_handle_irq_top_half;
+ camif_node->bottom_half_handler = cam_vfe_camif_handle_irq_bottom_half;
+
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
new file mode 100644
index 0000000..cc6aab0
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
@@ -0,0 +1,78 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_CAMIF_VER2_H_
+#define _CAM_VFE_CAMIF_VER2_H_
+
+#include "cam_isp_hw.h"
+#include "cam_vfe_top.h"
+
+struct cam_vfe_camif_ver2_reg {
+ uint32_t camif_cmd;
+ uint32_t camif_config;
+ uint32_t line_skip_pattern;
+ uint32_t pixel_skip_pattern;
+ uint32_t skip_period;
+ uint32_t irq_subsample_pattern;
+ uint32_t epoch_irq;
+ uint32_t raw_crop_width_cfg;
+ uint32_t raw_crop_height_cfg;
+ uint32_t reg_update_cmd;
+};
+
+struct cam_vfe_camif_reg_data {
+ uint32_t raw_crop_first_pixel_shift;
+ uint32_t raw_crop_first_pixel_mask;
+
+ uint32_t raw_crop_last_pixel_shift;
+ uint32_t raw_crop_last_pixel_mask;
+
+ uint32_t raw_crop_first_line_shift;
+ uint32_t raw_crop_first_line_mask;
+
+ uint32_t raw_crop_last_line_shift;
+ uint32_t raw_crop_last_line_mask;
+
+ uint32_t input_mux_sel_shift;
+ uint32_t input_mux_sel_mask;
+ uint32_t extern_reg_update_shift;
+ uint32_t extern_reg_update_mask;
+
+ uint32_t pixel_pattern_shift;
+ uint32_t pixel_pattern_mask;
+
+ uint32_t epoch_line_cfg;
+ uint32_t sof_irq_mask;
+ uint32_t epoch0_irq_mask;
+ uint32_t reg_update_irq_mask;
+};
+
+struct cam_vfe_camif_ver2_hw_info {
+ struct cam_vfe_top_ver2_reg_offset_common *common_reg;
+ struct cam_vfe_camif_ver2_reg *camif_reg;
+ struct cam_vfe_camif_reg_data *reg_data;
+};
+
+int cam_vfe_camif_ver2_acquire_resource(
+ struct cam_isp_resource_node *camif_res,
+ void *acquire_param);
+
+int cam_vfe_camif_process_cmd(void *priv,
+ uint32_t cmd_type, void *cmd_args, uint32_t arg_size);
+
+int cam_vfe_camif_ver2_init(
+ struct cam_hw_intf *hw_intf,
+ struct cam_hw_soc_info *soc_info,
+ void *camif_hw_info,
+ struct cam_isp_resource_node *camif_node);
+
+#endif /* _CAM_VFE_CAMIF_VER2_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c
new file mode 100644
index 0000000..e2bceb8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top.c
@@ -0,0 +1,38 @@
+/* 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 "cam_vfe_top.h"
+#include "cam_vfe_top_ver2.h"
+
+int cam_vfe_top_init(uint32_t top_version,
+ struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ void *top_hw_info,
+ struct cam_vfe_top **vfe_top)
+{
+ int rc = -EINVAL;
+
+ switch (top_version) {
+ case CAM_VFE_TOP_VER_2_0:
+ rc = cam_vfe_top_ver2_init(soc_info, hw_intf, top_hw_info,
+ vfe_top);
+ break;
+ default:
+ pr_err("Error! Unsupported Version %x\n", top_version);
+ break;
+ }
+
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
new file mode 100644
index 0000000..4fa5e98
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -0,0 +1,419 @@
+/* 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/slab.h>
+#include "cam_io_util.h"
+#include "cam_cdm_util.h"
+#include "cam_vfe_hw_intf.h"
+#include "cam_vfe_top.h"
+#include "cam_vfe_top_ver2.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+struct cam_vfe_top_ver2_common_data {
+ struct cam_hw_soc_info *soc_info;
+ struct cam_hw_intf *hw_intf;
+ struct cam_vfe_top_ver2_reg_offset_common *common_reg;
+};
+
+struct cam_vfe_top_ver2_priv {
+ struct cam_vfe_top_ver2_common_data common_data;
+ struct cam_vfe_camif *camif;
+ struct cam_isp_resource_node mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX];
+};
+
+static int cam_vfe_top_mux_get_base(struct cam_vfe_top_ver2_priv *top_priv,
+ void *cmd_args, uint32_t arg_size)
+{
+ uint32_t size = 0;
+ uint32_t mem_base = 0;
+ struct cam_isp_hw_get_cdm_args *cdm_args = cmd_args;
+ struct cam_cdm_utils_ops *cdm_util_ops = NULL;
+
+ if (arg_size != sizeof(struct cam_isp_hw_get_cdm_args)) {
+ pr_err("Error! Invalid cmd size\n");
+ return -EINVAL;
+ }
+
+ if (!cdm_args || !cdm_args->res || !top_priv ||
+ !top_priv->common_data.soc_info) {
+ pr_err("Error! Invalid args\n");
+ return -EINVAL;
+ }
+
+ cdm_util_ops =
+ (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops;
+
+ if (!cdm_util_ops) {
+ pr_err("Invalid CDM ops\n");
+ return -EINVAL;
+ }
+
+ size = cdm_util_ops->cdm_required_size_changebase();
+ /* since cdm returns dwords, we need to convert it into bytes */
+ if ((size * 4) > cdm_args->size) {
+ pr_err("buf size:%d is not sufficient, expected: %d\n",
+ cdm_args->size, size);
+ return -EINVAL;
+ }
+
+ mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(
+ top_priv->common_data.soc_info, VFE_CORE_BASE_IDX);
+ CDBG("core %d mem_base 0x%x\n", top_priv->common_data.soc_info->index,
+ mem_base);
+
+ cdm_util_ops->cdm_write_changebase(cdm_args->cmd_buf_addr, mem_base);
+ cdm_args->used_bytes = (size * 4);
+
+ return 0;
+}
+
+static int cam_vfe_top_mux_get_reg_update(
+ struct cam_vfe_top_ver2_priv *top_priv,
+ void *cmd_args, uint32_t arg_size)
+{
+ uint32_t size = 0;
+ uint32_t reg_val_pair[2];
+ struct cam_isp_hw_get_cdm_args *cdm_args = cmd_args;
+ struct cam_cdm_utils_ops *cdm_util_ops = NULL;
+
+ if (arg_size != sizeof(struct cam_isp_hw_get_cdm_args)) {
+ pr_err("Error! Invalid cmd size\n");
+ return -EINVAL;
+ }
+
+ if (!cdm_args || !cdm_args->res) {
+ pr_err("Error! Invalid args\n");
+ return -EINVAL;
+ }
+
+ cdm_util_ops = (struct cam_cdm_utils_ops *)cdm_args->res->cdm_ops;
+
+ if (!cdm_util_ops) {
+ pr_err("Error! Invalid CDM ops\n");
+ return -EINVAL;
+ }
+
+ size = cdm_util_ops->cdm_required_size_reg_random(1);
+ /* since cdm returns dwords, we need to convert it into bytes */
+ if ((size * 4) > cdm_args->size) {
+ pr_err("Error! buf size:%d is not sufficient, expected: %d\n",
+ cdm_args->size, size);
+ return -EINVAL;
+ }
+
+ reg_val_pair[0] = top_priv->common_data.common_reg->reg_update_cmd;
+
+ if (cdm_args->res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
+ reg_val_pair[1] = BIT(0);
+ else
+ reg_val_pair[1] = BIT(cdm_args->res->res_id + 1);
+
+ cdm_util_ops->cdm_write_regrandom(cdm_args->cmd_buf_addr,
+ 1, reg_val_pair);
+
+ cdm_args->used_bytes = size * 4;
+
+ return 0;
+}
+
+int cam_vfe_top_get_hw_caps(void *device_priv,
+ void *get_hw_cap_args, uint32_t arg_size)
+{
+ return 0;
+}
+
+int cam_vfe_top_init_hw(void *device_priv,
+ void *init_hw_args, uint32_t arg_size)
+{
+ return 0;
+}
+
+int cam_vfe_top_reset(void *device_priv,
+ void *reset_core_args, uint32_t arg_size)
+{
+ struct cam_vfe_top_ver2_priv *top_priv = device_priv;
+ struct cam_hw_soc_info *soc_info = NULL;
+ struct cam_vfe_top_ver2_reg_offset_common *reg_common = NULL;
+
+ if (!top_priv) {
+ pr_err("Invalid arguments\n");
+ return -EINVAL;
+ }
+
+ soc_info = top_priv->common_data.soc_info;
+ reg_common = top_priv->common_data.common_reg;
+
+ /* Mask All the IRQs except RESET */
+ cam_io_w_mb((1 << 31),
+ CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + 0x5C);
+
+ /* Reset HW */
+ cam_io_w_mb(0x00003F9F,
+ CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) +
+ reg_common->global_reset_cmd);
+
+ CDBG("Reset HW exit\n");
+ return 0;
+}
+
+int cam_vfe_top_reserve(void *device_priv,
+ void *reserve_args, uint32_t arg_size)
+{
+ struct cam_vfe_top_ver2_priv *top_priv;
+ struct cam_vfe_acquire_args *args;
+ struct cam_vfe_hw_vfe_in_acquire_args *acquire_args;
+ uint32_t i;
+ int rc = -EINVAL;
+
+ if (!device_priv || !reserve_args) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
+ args = (struct cam_vfe_acquire_args *)reserve_args;
+ acquire_args = &args->vfe_in;
+
+
+ for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+ if (top_priv->mux_rsrc[i].res_id == acquire_args->res_id &&
+ top_priv->mux_rsrc[i].res_state ==
+ CAM_ISP_RESOURCE_STATE_AVAILABLE) {
+
+ if (acquire_args->res_id == CAM_ISP_HW_VFE_IN_CAMIF) {
+ rc = cam_vfe_camif_ver2_acquire_resource(
+ &top_priv->mux_rsrc[i],
+ args);
+ if (rc)
+ break;
+ }
+
+ top_priv->mux_rsrc[i].cdm_ops = acquire_args->cdm_ops;
+ top_priv->mux_rsrc[i].tasklet_info = args->tasklet;
+ top_priv->mux_rsrc[i].res_state =
+ CAM_ISP_RESOURCE_STATE_RESERVED;
+ acquire_args->rsrc_node =
+ &top_priv->mux_rsrc[i];
+
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+
+}
+
+int cam_vfe_top_release(void *device_priv,
+ void *release_args, uint32_t arg_size)
+{
+ struct cam_vfe_top_ver2_priv *top_priv;
+ struct cam_isp_resource_node *mux_res;
+
+ if (!device_priv || !release_args) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
+ mux_res = (struct cam_isp_resource_node *)release_args;
+
+ if (mux_res->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
+ pr_err("Error! Resource in Invalid res_state :%d\n",
+ mux_res->res_state);
+ return -EINVAL;
+ }
+
+ mux_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
+
+ return 0;
+}
+
+int cam_vfe_top_start(void *device_priv,
+ void *start_args, uint32_t arg_size)
+{
+ struct cam_vfe_top_ver2_priv *top_priv;
+ struct cam_isp_resource_node *mux_res;
+ int rc = 0;
+
+ if (!device_priv || !start_args) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
+ mux_res = (struct cam_isp_resource_node *)start_args;
+
+ if (mux_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) {
+ rc = mux_res->start(mux_res);
+ } else if (mux_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0 &&
+ mux_res->res_id <= CAM_ISP_HW_VFE_IN_RDI3) {
+ mux_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
+ rc = 0;
+ } else {
+ pr_err("Invalid res id:%d\n", mux_res->res_id);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int cam_vfe_top_stop(void *device_priv,
+ void *stop_args, uint32_t arg_size)
+{
+ struct cam_vfe_top_ver2_priv *top_priv;
+ struct cam_isp_resource_node *mux_res;
+ int rc = 0;
+
+ if (!device_priv || !stop_args) {
+ pr_err("Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
+ mux_res = (struct cam_isp_resource_node *)stop_args;
+
+ if (mux_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) {
+ rc = mux_res->stop(mux_res);
+ } else if (mux_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0 &&
+ mux_res->res_id <= CAM_ISP_HW_VFE_IN_RDI3) {
+ mux_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
+ rc = 0;
+ } else {
+ pr_err("Invalid res id:%d\n", mux_res->res_id);
+ rc = -EINVAL;
+ }
+
+ return rc;
+
+}
+
+int cam_vfe_top_read(void *device_priv,
+ void *read_args, uint32_t arg_size)
+{
+ return -ENODEV;
+}
+
+int cam_vfe_top_write(void *device_priv,
+ void *write_args, uint32_t arg_size)
+{
+ return -ENODEV;
+}
+
+int cam_vfe_top_process_cmd(void *device_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ int rc = 0;
+ struct cam_vfe_top_ver2_priv *top_priv;
+
+ if (!device_priv || !cmd_args) {
+ pr_err("Error! Invalid arguments\n");
+ return -EINVAL;
+ }
+ top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
+
+ switch (cmd_type) {
+ case CAM_VFE_HW_CMD_GET_CHANGE_BASE:
+ rc = cam_vfe_top_mux_get_base(top_priv, cmd_args, arg_size);
+ break;
+ case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+ rc = cam_vfe_top_mux_get_reg_update(top_priv, cmd_args,
+ arg_size);
+ break;
+ default:
+ rc = -EINVAL;
+ pr_err("Error! Invalid cmd:%d\n", cmd_type);
+ break;
+ }
+
+ return rc;
+}
+
+int cam_vfe_top_ver2_init(
+ struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ void *top_hw_info,
+ struct cam_vfe_top **vfe_top_ptr)
+{
+ int i, j, rc = 0;
+ struct cam_vfe_top_ver2_priv *top_priv = NULL;
+ struct cam_vfe_top_ver2_hw_info *ver2_hw_info = top_hw_info;
+ struct cam_vfe_top *vfe_top;
+
+ vfe_top = kzalloc(sizeof(struct cam_vfe_top), GFP_KERNEL);
+ if (!vfe_top) {
+ CDBG("Error! Failed to alloc for vfe_top\n");
+ rc = -ENOMEM;
+ goto err_alloc_top;
+ }
+
+ top_priv = kzalloc(sizeof(struct cam_vfe_top_ver2_priv),
+ GFP_KERNEL);
+ if (!top_priv) {
+ CDBG("Error! Failed to alloc for vfe_top_priv\n");
+ rc = -ENOMEM;
+ goto err_alloc_priv;
+ }
+ vfe_top->top_priv = top_priv;
+
+ for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+ top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN;
+ top_priv->mux_rsrc[i].hw_intf = hw_intf;
+ top_priv->mux_rsrc[i].res_state =
+ CAM_ISP_RESOURCE_STATE_AVAILABLE;
+ if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) {
+ top_priv->mux_rsrc[i].res_id =
+ CAM_ISP_HW_VFE_IN_CAMIF;
+
+ rc = cam_vfe_camif_ver2_init(hw_intf, soc_info,
+ &ver2_hw_info->camif_hw_info,
+ &top_priv->mux_rsrc[i]);
+ if (rc)
+ goto err_mux_init;
+ } else {
+ /* set the RDI resource id */
+ top_priv->mux_rsrc[i].res_id =
+ CAM_ISP_HW_VFE_IN_RDI0 + j;
+ top_priv->mux_rsrc[i].res_priv = NULL;
+ j++;
+ }
+ }
+
+ vfe_top->hw_ops.get_hw_caps = cam_vfe_top_get_hw_caps;
+ vfe_top->hw_ops.init = cam_vfe_top_init_hw;
+ vfe_top->hw_ops.reset = cam_vfe_top_reset;
+ vfe_top->hw_ops.reserve = cam_vfe_top_reserve;
+ vfe_top->hw_ops.release = cam_vfe_top_release;
+ vfe_top->hw_ops.start = cam_vfe_top_start;
+ vfe_top->hw_ops.stop = cam_vfe_top_stop;
+ vfe_top->hw_ops.read = cam_vfe_top_read;
+ vfe_top->hw_ops.write = cam_vfe_top_write;
+ vfe_top->hw_ops.process_cmd = cam_vfe_top_process_cmd;
+ *vfe_top_ptr = vfe_top;
+
+ top_priv->common_data.soc_info = soc_info;
+ top_priv->common_data.hw_intf = hw_intf;
+ top_priv->common_data.common_reg = ver2_hw_info->common_reg;
+
+ return rc;
+
+err_mux_init:
+ kfree(vfe_top->top_priv);
+err_alloc_priv:
+ kfree(vfe_top);
+err_alloc_top:
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h
new file mode 100644
index 0000000..24301d7
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.h
@@ -0,0 +1,62 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_TOP_VER2_H_
+#define _CAM_VFE_TOP_VER2_H_
+
+#include "cam_vfe_camif_ver2.h"
+
+#define CAM_VFE_TOP_VER2_MUX_MAX 4
+
+enum cam_vfe_top_ver2_module_type {
+ CAM_VFE_TOP_VER2_MODULE_LENS,
+ CAM_VFE_TOP_VER2_MODULE_STATS,
+ CAM_VFE_TOP_VER2_MODULE_COLOR,
+ CAM_VFE_TOP_VER2_MODULE_ZOOM,
+ CAM_VFE_TOP_VER2_MODULE_MAX,
+};
+
+struct cam_vfe_top_ver2_reg_offset_module_ctrl {
+ uint32_t reset;
+ uint32_t cgc_ovd;
+ uint32_t enable;
+};
+
+struct cam_vfe_top_ver2_reg_offset_common {
+ uint32_t hw_version;
+ uint32_t hw_capability;
+ uint32_t lens_feature;
+ uint32_t stats_feature;
+ uint32_t color_feature;
+ uint32_t zoom_feature;
+ uint32_t global_reset_cmd;
+ struct cam_vfe_top_ver2_reg_offset_module_ctrl
+ *module_ctrl[CAM_VFE_TOP_VER2_MODULE_MAX];
+ uint32_t bus_cgc_ovd;
+ uint32_t core_cfg;
+ uint32_t three_D_cfg;
+ uint32_t violation_status;
+ uint32_t reg_update_cmd;
+};
+
+struct cam_vfe_top_ver2_hw_info {
+ struct cam_vfe_top_ver2_reg_offset_common *common_reg;
+ struct cam_vfe_camif_ver2_hw_info camif_hw_info;
+ uint32_t mux_type[CAM_VFE_TOP_VER2_MUX_MAX];
+};
+
+int cam_vfe_top_ver2_init(struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ void *top_hw_info,
+ struct cam_vfe_top **vfe_top);
+
+#endif /* _CAM_VFE_TOP_VER2_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h
new file mode 100644
index 0000000..44c046d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+#ifndef _CAM_VFE_TOP_H_
+#define _CAM_VFE_TOP_H_
+
+#include "cam_hw_intf.h"
+#include "cam_isp_hw.h"
+
+#define CAM_VFE_TOP_VER_1_0 0x100000
+#define CAM_VFE_TOP_VER_2_0 0x200000
+
+#define CAM_VFE_CAMIF_VER_1_0 0x10
+#define CAM_VFE_CAMIF_VER_2_0 0x20
+
+#define CAM_VFE_RDI_VER_1_0 0x1000
+
+struct cam_vfe_top {
+ void *top_priv;
+ struct cam_hw_ops hw_ops;
+};
+
+struct cam_vfe_camif {
+ void *camif_priv;
+ int (*start_resource)(void *priv,
+ struct cam_isp_resource_node *camif_res);
+ int (*stop_resource)(void *priv,
+ struct cam_isp_resource_node *camif_res);
+ int (*acquire_resource)(void *priv,
+ struct cam_isp_resource_node *camif_res,
+ void *acquire_param);
+ int (*release_resource)(void *priv,
+ struct cam_isp_resource_node *camif_res);
+ int (*process_cmd)(void *priv, uint32_t cmd_type, void *cmd_args,
+ uint32_t arg_size);
+};
+
+int cam_vfe_top_init(uint32_t top_version,
+ struct cam_hw_soc_info *soc_info,
+ struct cam_hw_intf *hw_intf,
+ void *top_hw_info,
+ struct cam_vfe_top **vfe_top);
+
+#endif /* _CAM_VFE_TOP_H_*/