/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
 */

#ifndef _CAM_COMMON_UTIL_H_
#define _CAM_COMMON_UTIL_H_

#include <linux/types.h>
#include <linux/kernel.h>

#include "cam_hw_mgr_intf.h"

#define CAM_BITS_MASK_SHIFT(x, mask, shift) (((x) & (mask)) >> shift)
#define CAM_36BIT_INTF_GET_IOVA_BASE(iova) ((iova) >> 8)
#define CAM_36BIT_INTF_GET_IOVA_OFFSET(iova) ((iova) & 0xff)

#define CAM_COMMON_MINI_DUMP_DEV_NUM      6
#define CAM_COMMON_MINI_DUMP_DEV_NAME_LEN 16
#define CAM_COMMON_MINI_DUMP_SIZE         10 * 1024 * 1024

#define CAM_COMMON_HW_DUMP_TAG_MAX_LEN 128
#define CAM_MAX_NUM_CCI_PAYLOAD_BYTES     11

#define CAM_COMMON_EVT_INJECT_MODULE_PARAM_MAX_LENGTH 4096
#define CAM_COMMON_EVT_INJECT_BUFFER_LEN              200
#define CAM_COMMON_EVT_INJECT_BUFFER_ERROR "Buffer_Error"
#define CAM_COMMON_EVT_INJECT_NOTIFY_EVENT "Notify_Event"
#define CAM_COMMON_IFE_NODE  "IFE"
#define CAM_COMMON_ICP_NODE  "IPE"
#define CAM_COMMON_JPEG_NODE "JPEG"
#define CAM_COMMON_TFE_NODE "TFE"

#define CAM_COMMON_NS_PER_MS              1000000ULL

#define PTR_TO_U64(ptr) ((uint64_t)(uintptr_t)ptr)
#define U64_TO_PTR(ptr) ((void *)(uintptr_t)ptr)

#define CAM_TRIGGER_PANIC(format, args...) panic("CAMERA - " format "\n", ##args)

#define CAM_GET_BOOT_TIMESTAMP(timestamp) ktime_get_boottime_ts64(&(timestamp))
#define CAM_GET_TIMESTAMP(timestamp) ktime_get_real_ts64(&(timestamp))
#define CAM_GET_TIMESTAMP_DIFF_IN_MICRO(ts_start, ts_end, diff_microsec)                     \
({                                                                                           \
	diff_microsec = 0;                                                                   \
	if (ts_end.tv_nsec >= ts_start.tv_nsec) {                                            \
		diff_microsec =                                                              \
			(ts_end.tv_nsec - ts_start.tv_nsec) / 1000;                          \
		diff_microsec +=                                                             \
			(ts_end.tv_sec - ts_start.tv_sec) * 1000 * 1000;                     \
	} else {                                                                             \
		diff_microsec =                                                              \
			(ts_end.tv_nsec +                                                    \
			(1000*1000*1000 - ts_start.tv_nsec)) / 1000;                         \
		diff_microsec +=                                                             \
			(ts_end.tv_sec - ts_start.tv_sec - 1) * 1000 * 1000;                 \
	}                                                                                    \
})

#define CAM_CONVERT_TIMESTAMP_FORMAT(ts, hrs, min, sec, ms)                                  \
({                                                                                           \
	uint64_t tmp = ((ts).tv_sec);                                                        \
	(ms) = ((ts).tv_nsec) / 1000000;                                                     \
	(sec) = do_div(tmp, 60);                                                             \
	(min) = do_div(tmp, 60);                                                             \
	(hrs) = do_div(tmp, 24);                                                             \
})

#define CAM_COMMON_WAIT_FOR_COMPLETION_TIMEOUT_ERRMSG(complete, timeout_jiffies, module_id,  \
	fmt, args...)                                                                        \
({                                                                                           \
	struct timespec64 start_time, end_time;                                              \
	unsigned long rem_jiffies;                                                           \
	start_time = ktime_to_timespec64(ktime_get());                                       \
	rem_jiffies = cam_common_wait_for_completion_timeout((complete), (timeout_jiffies)); \
	if (!rem_jiffies) {                                                                  \
		end_time = ktime_to_timespec64(ktime_get());                                 \
		CAM_ERR(module_id,                                                           \
		fmt " (timeout: %ums start: timestamp:[%lld.%06lld] end: timestamp:[%lld.%06lld])",\
		##args, jiffies_to_msecs(timeout_jiffies),                                   \
		start_time.tv_sec, (start_time.tv_nsec/NSEC_PER_USEC),                       \
		end_time.tv_sec, (end_time.tv_nsec/NSEC_PER_USEC));                          \
	}                                                                                    \
	rem_jiffies;                                                                         \
})

typedef unsigned long (*cam_common_mini_dump_cb) (void *dst,
	unsigned long len, void *priv_data);

/**
 * struct cam_common_mini_dump_dev_info
 * @dump_cb       : address of data dumped
 * @name          : Name of driver
 * @num_devs      : Number of device registerd
 * @is_registered : Bool to indicate if registered
 */
struct cam_common_mini_dump_dev_info {
	cam_common_mini_dump_cb  dump_cb[CAM_COMMON_MINI_DUMP_DEV_NUM];
	uint8_t                  name[CAM_COMMON_MINI_DUMP_DEV_NUM]
				    [CAM_COMMON_MINI_DUMP_DEV_NAME_LEN];
	void                    *priv_data[CAM_COMMON_MINI_DUMP_DEV_NUM];
	uint8_t                  num_devs;
	bool                     is_registered;
};

/**
 * struct cam_common_mini_dump_data
 * @link         : address of data dumped
 * @name         : Name of driver
 * @size         : Size dumped
 */
struct cam_common_mini_dump_data {
	void          *waddr[CAM_COMMON_MINI_DUMP_DEV_NUM];
	uint8_t        name[CAM_COMMON_MINI_DUMP_DEV_NUM][CAM_COMMON_MINI_DUMP_DEV_NAME_LEN];
	unsigned long  size[CAM_COMMON_MINI_DUMP_DEV_NUM];
};


typedef int (*cam_common_evt_inject_cb) (void *inject_args);

enum cam_common_evt_inject_str_id_type {
	CAM_COMMON_EVT_INJECT_BUFFER_ERROR_TYPE,
	CAM_COMMON_EVT_INJECT_NOTIFY_EVENT_TYPE
};

enum cam_common_evt_inject_hw_id {
	CAM_COMMON_EVT_INJECT_HW_IFE,
	CAM_COMMON_EVT_INJECT_HW_TFE,
	CAM_COMMON_EVT_INJECT_HW_ICP,
	CAM_COMMON_EVT_INJECT_HW_JPEG,
	CAM_COMMON_EVT_INJECT_HW_MAX
};

enum cam_common_evt_inject_common_param_pos {
	STRING_ID,
	HW_NAME,
	DEV_HDL,
	REQ_ID,
	COMMON_PARAM_MAX
};

enum cam_common_evt_inject_notify_event_pos {
	EVT_NOTIFY_TYPE,
	EVT_NOTIFY_PARAM_MAX
};

enum cam_evt_inject_buffer_error_event {
	SYNC_ERROR_CAUSE,
	BUFFER_ERROR_PARAM_MAX
};

enum cam_evt_inject_error_param_pos {
	ERR_PARAM_ERR_TYPE,
	ERR_PARAM_ERR_CODE,
	ERR_PARAM_MAX
};

enum cam_evt_inject_node_param_pos {
	EVENT_TYPE,
	EVENT_CAUSE,
	NODE_PARAM_MAX,
};

enum cam_evt_inject_pf_params_pos {
	PF_PARAM_CTX_FOUND,
	PF_PARAM_MAX
};

/**
 * struct cam_common_evt_inject_data
 * @buf_done_data: buf done data
 * @evt_params   : event params for the injected event
 */
struct cam_common_evt_inject_data {
	void                           *buf_done_data;
	struct cam_hw_inject_evt_param *evt_params;
};

typedef int (*cam_common_evt_inject_ops) (void *cam_ctx,
	struct cam_common_evt_inject_data *inject_evt);

/**
 * struct cam_common_inject_evt_param
 * @evt_params : injection event params
 * @dev_hdl    : device handle to match with ctx's dev_hdl
 * @hw_id      : hw to be injected with the event
 */
struct cam_common_inject_evt_param {
	struct list_head list;
	struct cam_hw_inject_evt_param evt_params;
	int32_t dev_hdl;
	uint8_t hw_id;
};

/**
 * struct cam_common_inject_evt_info
 * @evt_inject_cb      : address of callback
 * @active_err_ctx_list: list containing active evt inject requests
 * @num_hw_registered  : number of callbacks registered
 * @is_list_initialised: bool to check init for evt_inject list
 */
struct cam_common_inject_evt_info {
	cam_common_evt_inject_cb    evt_inject_cb[CAM_COMMON_EVT_INJECT_HW_MAX];
	struct list_head            active_evt_ctx_list;
	uint8_t                     num_hw_registered;
	bool                        is_list_initialised;
};

/**
 * struct cam_common_hw_dump_args
 * @req_id         : request id
 * @cpu_addr       : address where dumping will start from
 * @buf_len        : length of buffer where data is being dumped to
 * @offset         : buffer offset from cpu_addr after each item dump
 * @ctxt_to_hw_map : context to hw map
 * @is_dump_all    : flag to indicate if all information or just bw/clk rate
 * @
 */
struct cam_common_hw_dump_args {
	uint64_t                req_id;
	uintptr_t               cpu_addr;
	size_t                  buf_len;
	size_t                  offset;
	void                   *ctxt_to_hw_map;
	bool                    is_dump_all;
};

/**
 * struct cam_common_hw_dump_header
 * @tag        : string used by the parser to call parse functions
 * @size       : size of the header in the buffer
 * @word_size  : word size of the header
 * @
 */
struct cam_common_hw_dump_header {
	uint8_t   tag[CAM_COMMON_HW_DUMP_TAG_MAX_LEN];
	uint64_t  size;
	uint32_t  word_size;
};

/**
 * @brief                 release all event inject params in the g_inject_evt_info
 *                        for a specific dev_hdl
 * @dev_hdl:              device handle to which the evt inject params belong to
 */
void cam_common_release_evt_params(int32_t dev_hdl);

/**
 * cam_common_util_get_string_index()
 *
 * @brief                  Match the string from list of strings to return
 *                         matching index
 *
 * @strings:               Pointer to list of strings
 * @num_strings:           Number of strings in 'strings'
 * @matching_string:       String to match
 * @index:                 Pointer to index to return matching index
 *
 * @return:                0 for success
 *                         -EINVAL for Fail
 */
int cam_common_util_get_string_index(const char **strings,
	uint32_t num_strings, const char *matching_string, uint32_t *index);

/**
 * cam_common_util_remove_duplicate_arr()
 *
 * @brief                  Move all the unique integers to the start of
 *                         the array and return the number of unique integers
 *
 * @array:                 Pointer to the first integer of array
 * @num:                   Number of elements in array
 *
 * @return:                Number of unique integers in array
 */
uint32_t cam_common_util_remove_duplicate_arr(int32_t *array,
	uint32_t num);

/**
 * cam_common_wait_for_completion_timeout()
 *
 * @brief                  common interface to implement wait for completion
 *                         for slow environment like presil, single debug
 *                         timeout variable can take care
 *
 * @complete:              Pointer to the first integer of array
 * @timeout_jiffies:       Timeout value in jiffie
 *
 * @return:                Remaining jiffies, non-zero for success, zero
 *                         in case of failure
 */
unsigned long cam_common_wait_for_completion_timeout(
	struct completion   *complete,
	unsigned long        timeout_jiffies);
/**
 * cam_common_read_poll_timeout()
 *
 * @brief                  common interface to read poll timeout
 *
 * @addr:                  Address of IO register
 * @delay:                 Delay interval of poll
 * @timeout:               Timeout for poll
 * @mask:                  Mask to be checked
 * @check_val:             Value to be compared to break poll
 * @status:                Status of register of IO
 *
 * @return:                0 if success and negative if fail
 * */
int cam_common_read_poll_timeout(
	void __iomem        *addr,
	unsigned long        delay,
	unsigned long        timeout,
	uint32_t             mask,
	uint32_t             check_val,
	uint32_t            *status);

/**
 * cam_common_modify_timer()
 *
 * @brief                  common interface to modify timer,
 *
 * @timer:                 reference to system timer
 * @timeout_val:           timeout value for timer
 *
 * @return:                0 if success and negative if fail
 */
int cam_common_modify_timer(struct timer_list *timer, int32_t timeout_val);

/**
 * cam_common_util_thread_switch_delay_detect()
 *
 * @brief                  Detect if there is any scheduling delay
 *
 * @wq_name:               workq name
 * @state:                 either schedule or execution
 * @cb:                    callback scheduled or executed
 * @scheduled_time:        Time when workq or tasklet was scheduled
 * @threshold:             Threshold time
 *
 */
void cam_common_util_thread_switch_delay_detect(char *wq_name, const char *state,
	void *cb, ktime_t scheduled_time, uint32_t threshold);

/**
 * cam_common_register_mini_dump_cb()
 *
 * @brief                  common interface to register mini dump cb
 *
 * @mini_dump_cb:          Pointer to the mini_dump_cb
 * @name:                  name of device registering
 *
 * @return:                0 if success in register non-zero if failes
 */
#if IS_REACHABLE(CONFIG_QCOM_VA_MINIDUMP)
int cam_common_register_mini_dump_cb(
	cam_common_mini_dump_cb mini_dump_cb, uint8_t *dev_name, void *priv_data);
#else
static inline int cam_common_register_mini_dump_cb(
	cam_common_mini_dump_cb mini_dump_cb,
	uint8_t *dev_name, void *priv_data)
{
	return 0;
}
#endif

/**
 * cam_common_user_dump_clock()
 *
 * @brief                  Handles clock rate dump
 *
 * @dump_struct:           Struct holding dump info
 * @addr_ptr:              Pointer to buffer address pointer
 */
void *cam_common_user_dump_clock(
	void     *dump_struct,
	uint8_t  *addr_ptr);

/**
 * cam_common_user_dump_helper()
 *
 * @brief                  Handles buffer addressing and dumping for user dump
 *
 * @cmd_args:              Holds cam_common_hw_dump_args pointer
 * @func:                  Function pointer for dump function
 * @dump_struct:           Struct holding dump info
 * @size:                  Size_t value used for header word size
 * @tag:                   Tag for header, used by parser
 * @...:                   Variadic arguments, appended to tag if given
 */
int cam_common_user_dump_helper(
	void        *cmd_args,
	void        *(*func)(void *, uint8_t *),
	void        *dump_struct,
	size_t       size,
	const char  *tag,
	...);

/**
 * cam_common_register_evt_inject_cb()
 *
 * @brief                  common interface to register evt inject cb
 *
 * @evt_inject_cb:         Pointer to evt_inject_cb
 * @hw_id:                 HW id of the HW driver registering
 *
 * @return:                0 if success in register non-zero if failes
 */
int cam_common_register_evt_inject_cb(
	cam_common_evt_inject_cb evt_inject_cb,
	enum cam_common_evt_inject_hw_id hw_id);

#endif /* _CAM_COMMON_UTIL_H_ */