From caa6b2a1a4e3142caa22b973307713263d29c1d2 Mon Sep 17 00:00:00 2001 From: Tony Lijo Jose Date: Wed, 23 Jun 2021 00:38:06 +0530 Subject: [PATCH] msm: camera: sensor: Add tpg driver support Add new tpg subdev driver. This change exposes the tpg hw as a new subdev similar to that of a sensor driver. CRs-Fixed: 2973850 Change-Id: I6fdb4457d8cc829546896f26bdde8765a4258e7c Signed-off-by: Tony Lijo Jose --- Kbuild | 5 + drivers/cam_req_mgr/cam_req_mgr_interface.h | 2 + .../cam_sensor_utils/cam_sensor_cmn_header.h | 3 +- .../cam_sensor_module/cam_tpg/cam_tpg_core.c | 748 ++++++++++++++++++ .../cam_sensor_module/cam_tpg/cam_tpg_core.h | 39 + .../cam_sensor_module/cam_tpg/cam_tpg_dev.c | 381 +++++++++ .../cam_sensor_module/cam_tpg/cam_tpg_dev.h | 142 ++++ .../cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.c | 671 ++++++++++++++++ .../cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.h | 314 ++++++++ .../tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.c | 283 +++++++ .../tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.h | 85 ++ .../tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h | 25 + .../tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.c | 486 ++++++++++++ .../tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.h | 172 ++++ .../tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h | 27 + drivers/cam_utils/cam_debug_util.c | 3 + drivers/cam_utils/cam_debug_util.h | 1 + drivers/camera_main.c | 2 + drivers/camera_main.h | 2 + include/uapi/camera/media/cam_req_mgr.h | 1 + include/uapi/camera/media/cam_sensor.h | 239 ++++++ 21 files changed, 3630 insertions(+), 1 deletion(-) create mode 100644 drivers/cam_sensor_module/cam_tpg/cam_tpg_core.c create mode 100644 drivers/cam_sensor_module/cam_tpg/cam_tpg_core.h create mode 100644 drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.c create mode 100644 drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.h create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.c create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.h create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.c create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.h create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.c create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.h create mode 100644 drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h diff --git a/Kbuild b/Kbuild index 0b819178f1..8c1ebb0b3b 100644 --- a/Kbuild +++ b/Kbuild @@ -191,6 +191,11 @@ camera-$(CONFIG_SPECTRA_SENSOR) += \ drivers/cam_sensor_module/cam_cci/cam_cci_dev.o \ drivers/cam_sensor_module/cam_cci/cam_cci_core.o \ drivers/cam_sensor_module/cam_cci/cam_cci_soc.o \ + drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.o \ + drivers/cam_sensor_module/cam_tpg/cam_tpg_core.o \ + drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.o \ + drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.o \ + drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.o \ drivers/cam_sensor_module/cam_csiphy/cam_csiphy_soc.o \ drivers/cam_sensor_module/cam_csiphy/cam_csiphy_dev.o \ drivers/cam_sensor_module/cam_csiphy/cam_csiphy_core.o \ diff --git a/drivers/cam_req_mgr/cam_req_mgr_interface.h b/drivers/cam_req_mgr/cam_req_mgr_interface.h index 4be12ccf63..d72a3d31c6 100644 --- a/drivers/cam_req_mgr/cam_req_mgr_interface.h +++ b/drivers/cam_req_mgr/cam_req_mgr_interface.h @@ -186,6 +186,7 @@ enum cam_req_mgr_device_error { * @EXTERNAL_1 : third party device * @EXTERNAL_2 : third party device * @EXTERNAL_3 : third party device + * @TPG : Test Pattern Generator * @MAX : invalid device id */ enum cam_req_mgr_device_id { @@ -198,6 +199,7 @@ enum cam_req_mgr_device_id { CAM_REQ_MGR_DEVICE_EXTERNAL_1, CAM_REQ_MGR_DEVICE_EXTERNAL_2, CAM_REQ_MGR_DEVICE_EXTERNAL_3, + CAM_REQ_MGR_DEVICE_TPG, CAM_REQ_MGR_DEVICE_ID_MAX, }; diff --git a/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h index 42983fa1e0..ea8fadc512 100644 --- a/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h +++ b/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef _CAM_SENSOR_CMN_HEADER_ @@ -27,6 +27,7 @@ #define CAM_FLASH_NAME "cam-flash" #define CAM_EEPROM_NAME "cam-eeprom" #define CAM_OIS_NAME "cam-ois" +#define CAM_TPG_NAME "cam-tpg" #define MAX_SYSTEM_PIPELINE_DELAY 2 diff --git a/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.c b/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.c new file mode 100644 index 0000000000..b23a688e3a --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include "cam_tpg_core.h" +#include "cam_packet_util.h" +#include +#include "tpg_hw/tpg_hw.h" + +int cam_tpg_shutdown(struct cam_tpg_device *tpg_dev) +{ + if (tpg_dev != NULL) { + CAM_INFO(CAM_TPG, "TPG[%d] shutdown cleanup.", + tpg_dev->soc_info.index); + tpg_hw_reset(&tpg_dev->tpg_hw); + tpg_dev->state = CAM_TPG_STATE_INIT; + } + return 0; +} + +int cam_tpg_publish_dev_info( + struct cam_req_mgr_device_info *info) +{ + int rc = 0; + struct cam_tpg_device *tpg_dev = NULL; + + if (!info) + return -EINVAL; + + tpg_dev = (struct cam_tpg_device *) + cam_get_device_priv(info->dev_hdl); + + if (!tpg_dev) { + CAM_ERR(CAM_TPG, "Device data is NULL"); + return -EINVAL; + } + + info->dev_id = CAM_REQ_MGR_DEVICE_TPG; + strlcpy(info->name, CAM_TPG_NAME, sizeof(info->name)); + /* Hard code for now */ + info->p_delay = 2; + info->trigger = CAM_TRIGGER_POINT_SOF; + + return rc; +} + +int cam_tpg_setup_link( + struct cam_req_mgr_core_dev_link_setup *link) +{ + struct cam_tpg_device *tpg_dev = NULL; + + if (!link) + return -EINVAL; + + tpg_dev = (struct cam_tpg_device *) + cam_get_device_priv(link->dev_hdl); + if (!tpg_dev) { + CAM_ERR(CAM_TPG, "Device data is NULL"); + return -EINVAL; + } + + mutex_lock(&tpg_dev->mutex); + if (link->link_enable) { + tpg_dev->crm_intf.link_hdl = link->link_hdl; + tpg_dev->crm_intf.crm_cb = link->crm_cb; + CAM_DBG(CAM_TPG, "TPG[%d] CRM enable link done", tpg_dev->soc_info.index); + } else { + tpg_dev->crm_intf.link_hdl = -1; + tpg_dev->crm_intf.crm_cb = NULL; + CAM_DBG(CAM_TPG, "TPG[%d] CRM disable link done", tpg_dev->soc_info.index); + } + mutex_unlock(&tpg_dev->mutex); + return 0; +} + +static int cam_tpg_notify_frame_skip( + struct cam_req_mgr_apply_request *apply) +{ + CAM_DBG(CAM_TPG, "Got Skip frame from crm"); + return 0; +} + +static int cam_tpg_apply_req( + struct cam_req_mgr_apply_request *apply) +{ + if (!apply) { + CAM_ERR(CAM_TPG, "invalid parameters"); + return -EINVAL; + } + CAM_DBG(CAM_TPG, "Got Apply request from crm %lld", apply->request_id); + return 0; +} + +static int cam_tpg_flush_req( + struct cam_req_mgr_flush_request *flush) +{ + CAM_DBG(CAM_TPG, "Got Flush request from crm"); + return 0; +} + +static int cam_tpg_process_crm_evt( + struct cam_req_mgr_link_evt_data *event) +{ + + struct cam_tpg_device *tpg_dev = NULL; + if (!event) { + CAM_ERR(CAM_TPG, "Invalid argument"); + return -EINVAL; + } + + tpg_dev = cam_get_device_priv(event->dev_hdl); + if (!tpg_dev) { + CAM_ERR(CAM_TPG, "Invalid dev_hdl"); + return -EINVAL; + } + + switch(event->evt_type) { + case CAM_REQ_MGR_LINK_EVT_SOF_FREEZE: + tpg_hw_dump_status(&tpg_dev->tpg_hw); + break; + default: + CAM_DBG(CAM_TPG, "Got crm event notification: %d", event->evt_type); + break; + } + return 0; +} + +static int cam_tpg_dump_req( + struct cam_req_mgr_dump_info *dump_info) +{ + CAM_DBG(CAM_TPG, "Got dump request from CRM"); + return 0; +} + +int tpg_crm_intf_init( + struct cam_tpg_device *tpg_dev) +{ + if (tpg_dev == NULL) + return -EINVAL; + + tpg_dev->crm_intf.device_hdl = -1; + tpg_dev->crm_intf.link_hdl = -1; + tpg_dev->crm_intf.ops.get_dev_info = cam_tpg_publish_dev_info; + tpg_dev->crm_intf.ops.link_setup = cam_tpg_setup_link; + tpg_dev->crm_intf.ops.apply_req = cam_tpg_apply_req; + tpg_dev->crm_intf.ops.notify_frame_skip = + cam_tpg_notify_frame_skip; + tpg_dev->crm_intf.ops.flush_req = cam_tpg_flush_req; + tpg_dev->crm_intf.ops.process_evt = cam_tpg_process_crm_evt; + tpg_dev->crm_intf.ops.dump_req = cam_tpg_dump_req; + + return 0; +} + +static int __cam_tpg_handle_query_cap( + struct cam_tpg_device *tpg_dev, + struct cam_tpg_query_cap *query) +{ + struct cam_hw_soc_info *soc_info = NULL; + + if (!tpg_dev || !query) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + + soc_info = &tpg_dev->soc_info; + CAM_DBG(CAM_TPG, "Handling tpg query capability for %d slot: %d phy:%d", + soc_info->index, tpg_dev->slot_id, tpg_dev->phy_id); + query->slot_info = soc_info->index; + query->csiphy_slot_id = tpg_dev->phy_id; + query->version = 0x0; + if (tpg_dev->tpg_hw.hw_info) { + query->version = tpg_dev->tpg_hw.hw_info->version; + } else { + CAM_ERR(CAM_TPG, "Invalid hw info"); + return -EINVAL; + } + + return 0; +} + +static int __cam_tpg_handle_acquire_dev( + struct cam_tpg_device *tpg_dev, + struct cam_tpg_acquire_dev *acquire) +{ + int rc = 0; + struct cam_create_dev_hdl crm_intf_params; + + if (!tpg_dev || !acquire) { + CAM_ERR(CAM_TPG, "invalid input "); + rc = -EINVAL; + goto exit; + } + + if (tpg_dev->state != CAM_TPG_STATE_INIT) { + CAM_ERR(CAM_TPG, "TPG[%d] not in right state[%d] to acquire", + tpg_dev->soc_info.index, tpg_dev->state); + rc = -EINVAL; + goto exit; + } + + crm_intf_params.session_hdl = acquire->session_handle; + crm_intf_params.ops = &tpg_dev->crm_intf.ops; + crm_intf_params.v4l2_sub_dev_flag = 0; + crm_intf_params.media_entity_flag = 0; + crm_intf_params.priv = tpg_dev; + crm_intf_params.dev_id = CAM_TPG; + + acquire->device_handle = + cam_create_device_hdl(&crm_intf_params); + tpg_dev->crm_intf.device_hdl = acquire->device_handle; + tpg_dev->crm_intf.session_hdl = acquire->session_handle; + rc = tpg_hw_acquire(&tpg_dev->tpg_hw, (struct tpg_hw_acquire_args *)NULL); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] hw acquire failed", tpg_dev->soc_info.index); + cam_destroy_device_hdl(tpg_dev->crm_intf.device_hdl); + tpg_dev->crm_intf.device_hdl = -1; + tpg_dev->crm_intf.session_hdl = -1; + } else { + tpg_dev->state = CAM_TPG_STATE_ACQUIRE; + CAM_INFO(CAM_TPG, "TPG[%d] Acquire Device done", tpg_dev->soc_info.index); + } +exit: + return rc; +} + +static int __cam_tpg_handle_release_dev( + struct cam_tpg_device *tpg_dev, + struct cam_release_dev_cmd *release) +{ + int rc = 0; + + if (!release || !tpg_dev) { + CAM_ERR(CAM_TPG, "Invalid params"); + return -EINVAL; + } + + if (release->dev_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid device handle for context"); + return -EINVAL; + } + + if (release->session_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid session handle for context"); + return -EINVAL; + } + if (tpg_dev->state == CAM_TPG_STATE_INIT) { + CAM_WARN(CAM_TPG, "TPG[%d] not in right state[%d] to release", + tpg_dev->soc_info.index, tpg_dev->state); + return 0; + } + + if (tpg_dev->state == CAM_TPG_STATE_START) { + CAM_DBG(CAM_TPG, "TPG[%d] release from start state", + tpg_dev->soc_info.index); + rc = tpg_hw_stop(&tpg_dev->tpg_hw); + if (rc < 0) { + CAM_ERR(CAM_TPG, "TPG[%d] unable to stop tpg", + tpg_dev->soc_info.index); + return rc; + } + } + rc = tpg_hw_release(&tpg_dev->tpg_hw); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] hw release failed", + tpg_dev->soc_info.index); + } else { + cam_destroy_device_hdl(tpg_dev->crm_intf.device_hdl); + tpg_dev->crm_intf.device_hdl = -1; + tpg_dev->crm_intf.session_hdl = -1; + CAM_INFO(CAM_TPG, "TPG[%d] Release Done.", tpg_dev->soc_info.index); + tpg_dev->state = CAM_TPG_STATE_INIT; + } + + return rc; +} + +static int __cam_tpg_handle_start_dev( + struct cam_tpg_device *tpg_dev, + struct cam_start_stop_dev_cmd *start) +{ + int rc = 0; + if (!start || !tpg_dev) + return -EINVAL; + + if (start->dev_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid device handle for context"); + return -EINVAL; + } + + if (start->session_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid session handle for context"); + return -EINVAL; + } + if (tpg_dev->state != CAM_TPG_STATE_ACQUIRE) { + CAM_ERR(CAM_TPG, "TPG[%d] not in right state[%d] to start", + tpg_dev->soc_info.index, tpg_dev->state); + return -EINVAL; + } + rc = tpg_hw_start(&tpg_dev->tpg_hw); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] START_DEV failed", tpg_dev->soc_info.index); + } else { + tpg_dev->state = CAM_TPG_STATE_START; + CAM_INFO(CAM_TPG, "TPG[%d] START_DEV done.", tpg_dev->soc_info.index); + } + + return rc; +} + +static int __cam_tpg_handle_stop_dev( + struct cam_tpg_device *tpg_dev, + struct cam_start_stop_dev_cmd *stop) +{ + int rc = 0; + if (!stop || !tpg_dev) + return -EINVAL; + + if (stop->dev_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid device handle for context"); + return -EINVAL; + } + + if (stop->session_handle <= 0) { + CAM_ERR(CAM_TPG, "Invalid session handle for context"); + return -EINVAL; + } + if (tpg_dev->state != CAM_TPG_STATE_START) { + CAM_WARN(CAM_TPG, "TPG[%d] not in right state[%d] to stop", + tpg_dev->soc_info.index, tpg_dev->state); + } + rc = tpg_hw_stop(&tpg_dev->tpg_hw); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] STOP_DEV failed", tpg_dev->soc_info.index); + } else { + /* Free all allocated streams during stop dev */ + tpg_hw_free_streams(&tpg_dev->tpg_hw); + tpg_dev->state = CAM_TPG_STATE_ACQUIRE; + CAM_INFO(CAM_TPG, "TPG[%d] STOP_DEV done.", tpg_dev->soc_info.index); + } + + return rc; +} + +static int cam_tpg_validate_cmd_descriptor( + struct cam_cmd_buf_desc *cmd_desc, + uint32_t *cmd_type, uintptr_t *cmd_addr) +{ + int rc = 0; + uintptr_t generic_ptr; + size_t len_of_buff = 0; + uint32_t *cmd_buf = NULL; + struct tpg_command_header_t *cmd_header = NULL; + + if (!cmd_desc || !cmd_type || !cmd_addr) + return -EINVAL; + + rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, + &generic_ptr, &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_TPG, + "Failed to get cmd buf Mem address : %d", rc); + return rc; + } + + cmd_buf = (uint32_t *)generic_ptr; + cmd_buf += cmd_desc->offset / 4; + cmd_header = (struct tpg_command_header_t *)cmd_buf; + + if (len_of_buff < sizeof(struct tpg_command_header_t)) { + CAM_ERR(CAM_TPG, "Got invalid command descriptor of invalid cmd buffer size"); + rc = -EINVAL; + goto end; + } + + switch (cmd_header->cmd_type) { + case TPG_CMD_TYPE_GLOBAL_CONFIG: { + if (cmd_header->size != sizeof(struct tpg_global_config_t)) { + CAM_ERR(CAM_TPG, "Got invalid global config command recv: %d exp: %d", + cmd_header->size, + sizeof(struct tpg_global_config_t)); + rc = -EINVAL; + goto end; + } + CAM_INFO(CAM_TPG, "Got global config cmd"); + *cmd_type = TPG_CMD_TYPE_GLOBAL_CONFIG; + break; + } + case TPG_CMD_TYPE_STREAM_CONFIG: { + if (cmd_header->size != sizeof(struct tpg_stream_config_t)) { + CAM_ERR(CAM_TPG, "Got invalid stream config command recv: %d exp: %d", + cmd_header->size, + sizeof(struct tpg_stream_config_t)); + + rc = -EINVAL; + goto end; + } + CAM_INFO(CAM_TPG, "Got stream config cmd"); + *cmd_type = TPG_CMD_TYPE_STREAM_CONFIG; + break; + } + case TPG_CMD_TYPE_ILLUMINATION_CONFIG: { + if (cmd_header->size != sizeof(struct tpg_illumination_control)) { + CAM_ERR(CAM_TPG, "Got invalid illumination config command"); + rc = -EINVAL; + goto end; + } + *cmd_type = TPG_CMD_TYPE_ILLUMINATION_CONFIG; + break; + } + default: + rc = -EINVAL; + CAM_ERR(CAM_TPG, "invalid config command"); + goto end; + } + if ((ssize_t)cmd_desc->offset > (len_of_buff - cmd_header->size)) { + CAM_ERR(CAM_TPG, "cmd header offset mismatch"); + rc = -EINVAL; + } + + *cmd_addr = (uintptr_t)cmd_header; +end: + return rc; +} + +static int cam_tpg_cmd_buf_parse( + struct cam_tpg_device *tpg_dev, + struct cam_packet *packet) +{ + int rc = 0, i = 0; + struct cam_cmd_buf_desc *cmd_desc = NULL; + + if (!tpg_dev || !packet) + return -EINVAL; + + for (i = 0; i < packet->num_cmd_buf; i++) { + uint32_t cmd_type = TPG_CMD_TYPE_INVALID; + uintptr_t cmd_addr; + + cmd_desc = (struct cam_cmd_buf_desc *) + ((uint32_t *)&packet->payload + + (packet->cmd_buf_offset / 4) + + (i * (sizeof(struct cam_cmd_buf_desc)/4))); + + rc = cam_tpg_validate_cmd_descriptor(cmd_desc, + &cmd_type, &cmd_addr); + if (rc < 0) + goto end; + + switch (cmd_type) { + case TPG_CMD_TYPE_GLOBAL_CONFIG: + rc = tpg_hw_copy_global_config(&tpg_dev->tpg_hw, + (struct tpg_global_config_t *)cmd_addr); + break; + case TPG_CMD_TYPE_STREAM_CONFIG: { + rc = tpg_hw_add_stream(&tpg_dev->tpg_hw, + (struct tpg_stream_config_t *)cmd_addr); + break; + } + case TPG_CMD_TYPE_ILLUMINATION_CONFIG: + CAM_ERR(CAM_TPG, "TPG[%d] ILLUMINATION CONFIG not supported now ", + tpg_dev->soc_info.index); + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] invalid command %d", + tpg_dev->soc_info.index, + cmd_type); + rc = -EINVAL; + break; + } + } +end: + return rc; +} + +static int cam_tpg_packet_parse( + struct cam_tpg_device *tpg_dev, + struct cam_config_dev_cmd *config) +{ + int rc = 0; + uintptr_t generic_ptr; + size_t len_of_buff = 0, remain_len = 0; + struct cam_packet *csl_packet = NULL; + + rc = cam_mem_get_cpu_buf(config->packet_handle, + &generic_ptr, &len_of_buff); + if (rc < 0) { + CAM_ERR(CAM_TPG, "Failed in getting the packet: %d", rc); + return rc; + } + + if ((sizeof(struct cam_packet) > len_of_buff) || + ((size_t)config->offset >= len_of_buff - + sizeof(struct cam_packet))) { + CAM_ERR(CAM_TPG, + "Inval cam_packet struct size: %zu, len_of_buff: %zu", + sizeof(struct cam_packet), len_of_buff); + rc = -EINVAL; + goto end; + } + remain_len = len_of_buff; + remain_len -= (size_t)config->offset; + csl_packet = (struct cam_packet *)(generic_ptr + + (uint32_t)config->offset); + + if (cam_packet_util_validate_packet(csl_packet, + remain_len)) { + CAM_ERR(CAM_TPG, "Invalid packet params"); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_TPG, "TPG[%d] " + "CONFIG_DEV, Packet opcode = %d num_cmds: %d num_ios: %d num_patch: %d", + tpg_dev->soc_info.index, + (csl_packet->header.op_code & 0xFF), + csl_packet->num_cmd_buf, + csl_packet->num_io_configs, + csl_packet->num_patches); + switch ((csl_packet->header.op_code & 0xFF)) { + case CAM_TPG_PACKET_OPCODE_INITIAL_CONFIG: { + if (csl_packet->num_cmd_buf <= 0) { + CAM_ERR(CAM_TPG, "Expecting atleast one command in Init packet"); + rc = -EINVAL; + goto end; + } + rc = cam_tpg_cmd_buf_parse(tpg_dev, csl_packet); + if (rc < 0) { + CAM_ERR(CAM_TPG, "CMD buffer parse failed"); + goto end; + } + tpg_hw_config(&tpg_dev->tpg_hw, TPG_HW_CMD_INIT_CONFIG, NULL); + break; + } + case CAM_TPG_PACKET_OPCODE_NOP: { + struct cam_req_mgr_add_request add_req = {0}; + + CAM_DBG(CAM_TPG, "TPG[%d] NOP packet request id : %llu", + tpg_dev->soc_info.index, + csl_packet->header.request_id); + if ((tpg_dev->crm_intf.link_hdl != -1) && + (tpg_dev->crm_intf.device_hdl != -1) && + (tpg_dev->crm_intf.crm_cb != NULL)) { + add_req.link_hdl = tpg_dev->crm_intf.link_hdl; + add_req.dev_hdl = tpg_dev->crm_intf.device_hdl; + add_req.req_id = csl_packet->header.request_id; + tpg_dev->crm_intf.crm_cb->add_req(&add_req); + } else { + CAM_ERR(CAM_TPG, "TPG[%d] invalid link req: %llu", + tpg_dev->soc_info.index, + csl_packet->header.request_id); + } + break; + } + default: + CAM_ERR(CAM_TPG, "TPG[%d] Invalid packet %x", + tpg_dev->soc_info.index, + (csl_packet->header.op_code & 0xFF)); + rc = -EINVAL; + break; + } +end: + return rc; +} + +static int __cam_tpg_handle_config_dev( + struct cam_tpg_device *tpg_dev, + struct cam_config_dev_cmd *config) +{ + int rc = 0; + + if (!config || !tpg_dev) + return -EINVAL; + + if (config->dev_handle <= 0) { + CAM_ERR(CAM_TPG, "TPG[%d] Invalid device handle", + tpg_dev->soc_info.index); + return -EINVAL; + } + + if (config->session_handle <= 0) { + CAM_ERR(CAM_TPG, "TPG[%d] Invalid session handle", + tpg_dev->soc_info.index); + return -EINVAL; + } + + if (tpg_dev->state < CAM_TPG_STATE_ACQUIRE) { + CAM_ERR(CAM_TPG, "TPG[%d] not in right state[%d] to configure", + tpg_dev->soc_info.index, tpg_dev->state); + } + // Handle Config Dev + rc = cam_tpg_packet_parse(tpg_dev, config); + return rc; +} + +static int validate_ioctl_params( + struct cam_tpg_device *tpg_dev, + struct cam_control *cmd) +{ + int rc = 0; + + if (!tpg_dev || !cmd) { + CAM_ERR(CAM_TPG, "Invalid input args"); + rc = -EINVAL; + goto exit; + } + + if (cmd->handle_type != CAM_HANDLE_USER_POINTER) { + CAM_ERR(CAM_TPG, "TPG[%d] Invalid handle type: %d", + tpg_dev->soc_info.index, + cmd->handle_type); + rc = -EINVAL; + } + CAM_DBG(CAM_TPG, "TPG[%d] Opcode: %d", tpg_dev->soc_info.index, cmd->op_code); +exit: + return rc; +} + +int cam_tpg_core_cfg( + struct cam_tpg_device *tpg_dev, + void *arg) +{ + int rc = 0; + struct cam_control *cmd = (struct cam_control *)arg; + + rc = validate_ioctl_params(tpg_dev, cmd); + if (rc < 0) + return rc; + + mutex_lock(&tpg_dev->mutex); + switch (cmd->op_code) { + case CAM_QUERY_CAP: { + struct cam_tpg_query_cap query = {0}; + + if (copy_from_user(&query, u64_to_user_ptr(cmd->handle), + sizeof(query))) { + rc = -EFAULT; + break; + } + + rc = __cam_tpg_handle_query_cap(tpg_dev, &query); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] querycap is failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + break; + } + + if (copy_to_user(u64_to_user_ptr(cmd->handle), &query, + sizeof(query))) + rc = -EFAULT; + + break; + } + case CAM_ACQUIRE_DEV: { + struct cam_tpg_acquire_dev acquire = {0}; + + if (copy_from_user(&acquire, u64_to_user_ptr(cmd->handle), + sizeof(acquire))) { + rc = -EFAULT; + break; + } + rc = __cam_tpg_handle_acquire_dev(tpg_dev, &acquire); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] acquire device failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + break; + } + if (copy_to_user(u64_to_user_ptr(cmd->handle), &acquire, + sizeof(acquire))) + rc = -EFAULT; + break; + } + case CAM_RELEASE_DEV: { + struct cam_release_dev_cmd release; + + if (copy_from_user(&release, u64_to_user_ptr(cmd->handle), + sizeof(release))) + rc = -EFAULT; + else { + rc = __cam_tpg_handle_release_dev(tpg_dev, &release); + if (rc) + CAM_ERR(CAM_TPG, + "TPG[%d] release device failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + } + break; + } + case CAM_START_DEV: { + struct cam_start_stop_dev_cmd start; + + if (copy_from_user(&start, u64_to_user_ptr(cmd->handle), + sizeof(start))) + rc = -EFAULT; + else { + rc = __cam_tpg_handle_start_dev(tpg_dev, &start); + if (rc) + CAM_ERR(CAM_TPG, + "TPG[%d] start device failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + } + break; + } + case CAM_STOP_DEV: { + struct cam_start_stop_dev_cmd stop; + + if (copy_from_user(&stop, u64_to_user_ptr(cmd->handle), + sizeof(stop))) + rc = -EFAULT; + else { + rc = __cam_tpg_handle_stop_dev(tpg_dev, &stop); + if (rc) + CAM_ERR(CAM_TPG, + "TPG[%d] stop device failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + } + break; + + } + case CAM_CONFIG_DEV: { + struct cam_config_dev_cmd config; + + if (copy_from_user(&config, u64_to_user_ptr(cmd->handle), + sizeof(config))) + rc = -EFAULT; + else { + rc = __cam_tpg_handle_config_dev(tpg_dev, &config); + if (rc) + CAM_ERR(CAM_TPG, + "TPG[%d] config device failed(rc = %d)", + tpg_dev->soc_info.index, + rc); + } + break; + } + default: + CAM_ERR(CAM_TPG, "Invalid ioctl : %d", cmd->op_code); + rc = -EINVAL; + break; + } + mutex_unlock(&tpg_dev->mutex); + return rc; +} diff --git a/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.h b/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.h new file mode 100644 index 0000000000..2cb0737054 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/cam_tpg_core.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __CAM_TPG_CORE_H__ +#define __CAM_TPG_CORE_H__ + +#include "cam_tpg_dev.h" + +/** + * @brief : do clean up of driver on user space process restart + * + * @param tpg_dev: tpg device + * + * @return : 0 on success + */ +int cam_tpg_shutdown(struct cam_tpg_device *tpg_dev); + +/** + * @brief initialize crm interface + * + * @param tpg_dev: tpg device instance + * + * @return : 0 on success + */ +int tpg_crm_intf_init(struct cam_tpg_device *tpg_dev); + +/** + * @brief : handle tpg device configuration + * + * @param tpg_dev : tpg device instance + * @param arg : configuration argument + * + * @return : 0 on success + */ +int32_t cam_tpg_core_cfg(struct cam_tpg_device *tpg_dev, void *arg); + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.c b/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.c new file mode 100644 index 0000000000..fb18e036a1 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include "cam_tpg_dev.h" +#include "cam_tpg_core.h" +#include "camera_main.h" +#include "tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h" +#include "tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h" + +static long cam_tpg_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct cam_tpg_device *tpg_dev = v4l2_get_subdevdata(sd); + int rc = 0; + + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_tpg_core_cfg(tpg_dev, arg); + break; + default: + CAM_ERR(CAM_TPG, "Wrong ioctl : %d", cmd); + rc = -ENOIOCTLCMD; + break; + } + + return rc; +} + + +#ifdef CONFIG_COMPAT +static long cam_tpg_subdev_compat_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + int32_t rc = 0; + struct cam_control cmd_data; + + if (copy_from_user(&cmd_data, (void __user *)arg, + sizeof(cmd_data))) { + CAM_ERR(CAM_TPG, "Failed to copy from user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + return -EFAULT; + } + + /* All the arguments converted to 64 bit here + * Passed to the api in core.c + */ + switch (cmd) { + case VIDIOC_CAM_CONTROL: + rc = cam_tpg_subdev_ioctl(sd, cmd, &cmd_data); + if (rc) + CAM_ERR(CAM_TPG, + "Failed in subdev_ioctl: %d", rc); + break; + default: + CAM_ERR(CAM_TPG, "Invalid compat ioctl cmd: %d", cmd); + rc = -ENOIOCTLCMD; + break; + } + + if (!rc) { + if (copy_to_user((void __user *)arg, &cmd_data, + sizeof(cmd_data))) { + CAM_ERR(CAM_TPG, + "Failed to copy to user_ptr=%pK size=%zu", + (void __user *)arg, sizeof(cmd_data)); + rc = -EFAULT; + } + } + + return rc; +} +#endif + + +static struct v4l2_subdev_core_ops tpg_subdev_core_ops = { + .ioctl = cam_tpg_subdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = cam_tpg_subdev_compat_ioctl, +#endif +}; + +static int cam_tpg_subdev_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct cam_tpg_device *tpg_dev = + v4l2_get_subdevdata(sd); + + if (!tpg_dev) { + CAM_ERR(CAM_TPG, "tpg_dev ptr is NULL"); + return -EINVAL; + } + + mutex_lock(&tpg_dev->mutex); + cam_tpg_shutdown(tpg_dev); + mutex_unlock(&tpg_dev->mutex); + + return 0; +} + +static const struct v4l2_subdev_ops tpg_subdev_ops = { + .core = &tpg_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops tpg_subdev_intern_ops = { + .close = cam_tpg_subdev_close, +}; + +irqreturn_t cam_tpg_irq_handler(int irq_num, void *data) +{ + CAM_DBG(CAM_TPG, "tpg irq handler"); + return IRQ_HANDLED; +} + + +static int tpg_subdev_init(struct cam_tpg_device *tpg_dev, + struct device *dev) +{ + int32_t rc = 0; + struct platform_device *pdev = to_platform_device(dev); + + tpg_dev->tpg_subdev.pdev = pdev; + tpg_dev->tpg_subdev.internal_ops = &tpg_subdev_intern_ops; + tpg_dev->tpg_subdev.ops = &tpg_subdev_ops; + tpg_dev->tpg_subdev.name = tpg_dev->device_name; + tpg_dev->tpg_subdev.sd_flags = + (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + tpg_dev->tpg_subdev.ent_function = CAM_TPG_DEVICE_TYPE; + tpg_dev->tpg_subdev.msg_cb = NULL; + tpg_dev->tpg_subdev.token = tpg_dev; + + rc = cam_register_subdev(&(tpg_dev->tpg_subdev)); + if (rc) + CAM_ERR(CAM_TPG, "cam_register_subdev Failed rc: %d", rc); + else + CAM_DBG(CAM_TPG, "TPG subdev init done"); + return rc; + +} + +static int tpg_soc_info_init(struct cam_tpg_device *tpg_dev, + struct device *dev) +{ + int32_t rc = 0; + struct platform_device *pdev = to_platform_device(dev); + struct device_node *of_node = NULL; + + tpg_dev->soc_info.pdev = pdev; + tpg_dev->soc_info.dev = &pdev->dev; + tpg_dev->soc_info.dev_name = pdev->name; + if (!dev || !tpg_dev) + return -EINVAL; + + of_node = dev->of_node; + + rc = cam_soc_util_get_dt_properties(&tpg_dev->soc_info); + if (rc < 0) { + CAM_ERR(CAM_CSIPHY, "parsing common soc dt(rc %d)", rc); + return rc; + } + + rc = of_property_read_u32(of_node, "phy-id", &(tpg_dev->phy_id)); + if (rc) { + CAM_ERR(CAM_TPG, "device %s failed to read phy-id", + tpg_dev->soc_info.dev_name); + return rc; + } + + rc = cam_soc_util_request_platform_resource( + &tpg_dev->soc_info, + cam_tpg_irq_handler, + tpg_dev); + if (rc) + CAM_ERR(CAM_TPG, "unable to request platfrom resources"); + else + CAM_DBG(CAM_TPG, "TPG dt parse done"); + + return rc; +} + +static int tpg_register_cpas_client(struct cam_tpg_device *tpg_dev, + struct device *dev) +{ + int32_t rc = 0; + struct cam_cpas_register_params cpas_parms; + struct platform_device *pdev = to_platform_device(dev); + + cpas_parms.cam_cpas_client_cb = NULL; + cpas_parms.cell_index = tpg_dev->soc_info.index; + cpas_parms.dev = &pdev->dev; + cpas_parms.userdata = tpg_dev; + + strlcpy(cpas_parms.identifier, "tpg", CAM_HW_IDENTIFIER_LENGTH); + + rc = cam_cpas_register_client(&cpas_parms); + if (rc) { + CAM_ERR(CAM_TPG, "CPAS registration failed rc: %d", rc); + return rc; + } + + tpg_dev->cpas_handle = cpas_parms.client_handle; + CAM_DBG(CAM_TPG, "CPAS registration successful handle=%d", + cpas_parms.client_handle); + + return rc; +} + +static int cam_tpg_hw_layer_init(struct cam_tpg_device *tpg_dev, + struct device *dev) +{ + int i = 0; + /* get top tpg hw information */ + const struct of_device_id *match_dev = NULL; + struct platform_device *pdev = to_platform_device(dev); + + match_dev = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + if (!match_dev) { + CAM_ERR(CAM_TPG, "No matching table for the top tpg hw"); + return -EINVAL; + } + + tpg_dev->tpg_hw.hw_idx = tpg_dev->soc_info.index; + tpg_dev->tpg_hw.hw_info = (struct tpg_hw_info *)match_dev->data; + tpg_dev->tpg_hw.soc_info = &tpg_dev->soc_info; + tpg_dev->tpg_hw.cpas_handle = tpg_dev->cpas_handle; + mutex_init(&tpg_dev->tpg_hw.mutex); + + tpg_dev->tpg_hw.vc_slots = devm_kzalloc(&pdev->dev, + sizeof(struct tpg_vc_slot_info) * tpg_dev->tpg_hw.hw_info->max_vc_channels, + GFP_KERNEL); + if (!tpg_dev->tpg_hw.vc_slots) { + CAM_ERR(CAM_TPG, "TPG VC slot allocation failed"); + mutex_destroy(&tpg_dev->tpg_hw.mutex); + return -ENOMEM; + } + + for(i = 0; i < tpg_dev->tpg_hw.hw_info->max_vc_channels; i++) { + tpg_dev->tpg_hw.vc_slots[i].slot_id = i; + tpg_dev->tpg_hw.vc_slots[i].vc = -1; + tpg_dev->tpg_hw.vc_slots[i].stream_count = 0; + INIT_LIST_HEAD(&(tpg_dev->tpg_hw.vc_slots[i].head)); + } + + return 0; +} + +static int cam_tpg_component_bind(struct device *dev, + struct device *master_dev, void *data) +{ + int rc = 0; + struct cam_tpg_device *tpg_dev = NULL; + struct platform_device *pdev = to_platform_device(dev); + + tpg_dev = devm_kzalloc(&pdev->dev, + sizeof(struct cam_tpg_device), GFP_KERNEL); + if (!tpg_dev) { + CAM_ERR(CAM_TPG, "TPG dev allocation failed"); + return -ENOMEM; + } + + strlcpy(tpg_dev->device_name, CAMX_TPG_DEV_NAME, + sizeof(tpg_dev->device_name)); + mutex_init(&tpg_dev->mutex); + tpg_dev->tpg_subdev.pdev = pdev; + tpg_dev->state = CAM_TPG_STATE_INIT; + rc = tpg_subdev_init(tpg_dev, dev); + if (rc < 0) { + CAM_ERR(CAM_TPG, "subdev init failed"); + goto bind_error_exit; + } + + rc = tpg_soc_info_init(tpg_dev, dev); + if (rc < 0) { + CAM_ERR(CAM_TPG, "soc init failed"); + goto release_subdev; + } + + rc = tpg_register_cpas_client(tpg_dev, dev); + if (rc < 0) { + CAM_ERR(CAM_TPG, "cpas register failed"); + goto release_subdev; + } + tpg_crm_intf_init(tpg_dev); + rc = cam_tpg_hw_layer_init(tpg_dev, dev); + if (rc < 0) { + CAM_ERR(CAM_TPG, "Hw layer init failed"); + goto release_subdev; + } + + return rc; + +release_subdev: + cam_unregister_subdev(&(tpg_dev->tpg_subdev)); +bind_error_exit: + mutex_destroy(&tpg_dev->mutex); + return rc; +} + +static void cam_tpg_component_unbind(struct device *dev, + struct device *master_dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct cam_tpg_device *tpg_dev = v4l2_get_subdevdata(subdev); + + CAM_INFO(CAM_TPG, "Unbind TPG component"); + cam_cpas_unregister_client(tpg_dev->cpas_handle); + cam_soc_util_release_platform_resource(&tpg_dev->soc_info); + mutex_destroy(&tpg_dev->mutex); + mutex_destroy(&tpg_dev->tpg_hw.mutex); + platform_set_drvdata(pdev, NULL); + v4l2_set_subdevdata(&(tpg_dev->tpg_subdev.sd), NULL); + cam_unregister_subdev(&(tpg_dev->tpg_subdev)); +} + +const static struct component_ops cam_tpg_component_ops = { + .bind = cam_tpg_component_bind, + .unbind = cam_tpg_component_unbind, +}; + +static int32_t cam_tpg_platform_probe(struct platform_device *pdev) +{ + int rc = 0; + + CAM_DBG(CAM_TPG, "Adding TPG component"); + rc = component_add(&pdev->dev, &cam_tpg_component_ops); + if (rc) + CAM_ERR(CAM_TPG, "failed to add component rc: %d", rc); + + return rc; +} + + +static int32_t cam_tpg_device_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &cam_tpg_component_ops); + return 0; +} + +static const struct of_device_id cam_tpg_dt_match[] = { + { + .compatible = "qcom,cam-tpg101", + .data = &tpg_v_1_0_hw_info, + }, + { + .compatible = "qcom,cam-tpg103", + .data = &tpg_v_1_3_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, cam_tpg_dt_match); + +struct platform_driver cam_tpg_driver = { + .probe = cam_tpg_platform_probe, + .remove = cam_tpg_device_remove, + .driver = { + .name = CAMX_TPG_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = cam_tpg_dt_match, + .suppress_bind_attrs = true, + }, +}; + +int32_t cam_tpg_init_module(void) +{ + CAM_DBG(CAM_TPG, "tpg module init"); + return platform_driver_register(&cam_tpg_driver); +} + +void cam_tpg_exit_module(void) +{ + CAM_DBG(CAM_TPG, "tpg exit module"); + platform_driver_unregister(&cam_tpg_driver); +} + +MODULE_DESCRIPTION("CAM TPG driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.h b/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.h new file mode 100644 index 0000000000..27d1d827bf --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/cam_tpg_dev.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __CAM_TPG_DEV_H__ +#define __CAM_TPG_DEV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_debug_util.h" +#include "cam_context.h" +#include "cam_soc_util.h" +#include +#include "tpg_hw/tpg_hw.h" + +#define CAMX_TPG_DEV_NAME "cam-tpg-driver" + +struct cam_tpg_device; +struct tpg_hw; + +struct cam_tpg_ioctl_ops { + int (*acquire_dev)(struct cam_tpg_device *tpg_dev, + struct cam_acquire_dev_cmd *cmd); + int (*release_dev)(struct cam_tpg_device *tpg_dev, + struct cam_release_dev_cmd *cmd); + int (*config_dev)(struct cam_tpg_device *tpg_dev, + struct cam_config_dev_cmd *cmd); + int (*start_dev)(struct cam_tpg_device *tpg_dev, + struct cam_start_stop_dev_cmd *cmd); + int (*stop_dev)(struct cam_tpg_device *tpg_dev, + struct cam_start_stop_dev_cmd *cmd); + int (*flush_dev)(struct cam_tpg_device *tpg_dev, + struct cam_flush_dev_cmd *cmd); + int (*acquire_hw)(struct cam_tpg_device *tpg_dev, void *args); + int (*release_hw)(struct cam_tpg_device *tpg_dev, void *args); +}; + +struct cam_tpg_crm_ops { + int (*get_dev_info)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_device_info *device_info); + int (*link)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_core_dev_link_setup *link); + int (*unlink)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_core_dev_link_setup *unlink); + int (*apply_req)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_apply_request *apply); + int (*notify_frame_skip)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_apply_request *apply); + int (*flush_req)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_flush_request *flush); + int (*process_evt)(struct cam_tpg_device *tpg_dev, + struct cam_req_mgr_link_evt_data *evt_data); +}; + +struct tpg_device_ops { + struct cam_tpg_ioctl_ops ioctl_ops; + struct cam_tpg_crm_ops crm_ops; +}; + +enum cam_tpg_state { + CAM_TPG_STATE_INIT, + CAM_TPG_STATE_ACQUIRE, + CAM_TPG_STATE_START, + CAM_TPG_STATE_STATE_MAX +}; + +/** + * struct tpg_crm_intf_params + * @device_hdl: Device Handle + * @session_hdl: Session Handle + * @link_hdl: Link Handle + * @ops: KMD operations + * @crm_cb: Callback API pointers + */ +struct tpg_crm_intf_params { + int32_t device_hdl; + int32_t session_hdl; + int32_t link_hdl; + struct cam_req_mgr_kmd_ops ops; + struct cam_req_mgr_crm_cb *crm_cb; +}; + +/** + * cam_tpg_device + * + * @device_name: tpg device name + * @mutex: mutex object for private use + * @tpg_subdev: tpg subdevice instance + * @soc_info: tpg soc infrastructure + * @cpas_handle: cpas handle + * @state_machine: tpg state machine + * @crm_intf: crm interface + * @tpg_hw : tpg hw instance + * @state : state machine states + * @slot_id : slot index of this tpg + * @phy_id : phy index mapped to this tpg + */ +struct cam_tpg_device { + char device_name[CAM_CTX_DEV_NAME_MAX_LENGTH]; + struct mutex mutex; + struct cam_subdev tpg_subdev; + struct cam_hw_soc_info soc_info; + uint32_t cpas_handle; + struct tpg_device_ops state_machine[CAM_TPG_STATE_STATE_MAX]; + struct tpg_crm_intf_params crm_intf; + struct tpg_hw tpg_hw; + int state; + int slot_id; + int phy_id; +}; + + +/** + * @brief : tpg module init + * + * @return : 0 on success + */ +int32_t cam_tpg_init_module(void); + +/** + * @brief : tpg module exit + */ +void cam_tpg_exit_module(void); + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.c b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.c new file mode 100644 index 0000000000..5f102353d6 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include "tpg_hw.h" + +#define BYTES_PER_REGISTER 4 +#define NUM_REGISTER_PER_LINE 4 +#define REG_OFFSET(__start, __i) ((__start) + ((__i) * BYTES_PER_REGISTER)) + +static int cam_io_tpg_dump(void __iomem *base_addr, + uint32_t start_offset, int size) +{ + char line_str[128]; + char *p_str; + int i; + uint32_t data; + + CAM_DBG(CAM_TPG, "addr=%pK offset=0x%x size=%d", + base_addr, start_offset, size); + + if (!base_addr || (size <= 0)) + return -EINVAL; + + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size; i++) { + if (i % NUM_REGISTER_PER_LINE == 0) { + snprintf(p_str, 12, "0x%08x: ", + REG_OFFSET(start_offset, i)); + p_str += 11; + } + data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i)); + snprintf(p_str, 9, "%08x ", data); + p_str += 8; + if ((i + 1) % NUM_REGISTER_PER_LINE == 0) { + CAM_DBG(CAM_TPG, "%s", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + CAM_ERR(CAM_TPG, "%s", line_str); + + return 0; +} + +int32_t cam_tpg_mem_dmp(struct cam_hw_soc_info *soc_info) +{ + int32_t rc = 0; + resource_size_t size = 0; + void __iomem *addr = NULL; + + if (!soc_info) { + rc = -EINVAL; + CAM_ERR(CAM_TPG, "invalid input %d", rc); + return rc; + } + addr = soc_info->reg_map[0].mem_base; + size = resource_size(soc_info->mem_block[0]); + rc = cam_io_tpg_dump(addr, 0, (size >> 2)); + if (rc < 0) { + CAM_ERR(CAM_TPG, "generating dump failed %d", rc); + } + return rc; +} + + +#define __TPG_DEBUG_DUMP__ +#ifdef __TPG_DEBUG_DUMP__ +static const char * const tpg_phy_type_strings[] = { + "TPG_PHY_TYPE_INVALID", + "TPG_PHY_TYPE_DPHY", + "TPG_PHY_TYPE_CPHY", + "TPG_PHY_TYPE_MAX" +}; + +static const char * const tpg_interleaving_format_string[] = { + "TPG_INTERLEAVING_FORMAT_INVALID", + "TPG_INTERLEAVING_FORMAT_FRAME", + "TPG_INTERLEAVING_FORMAT_LINE", + "TPG_INTERLEAVING_FORMAT_MAX" +}; + +static const char * const tpg_shutter_type_strings[] = { + "TPG_SHUTTER_TYPE_INVALID", + "TPG_SHUTTER_TYPE_ROLLING", + "TPG_SHUTTER_TYPE_GLOBAL", + "TPG_SHUTTER_TYPE_MAX" +}; + +static const char *const tpg_pattern_type_strings[] = { + "TPG_PATTERN_INVALID", + "TPG_PATTERN_REAL_IMAGE", + "TPG_PATTERN_RANDOM_PIXL", + "TPG_PATTERN_RANDOM_INCREMENTING_PIXEL", + "TPG_PATTERN_COLOR_BAR", + "TPG_PATTERN_ALTERNATING_55_AA", + "TPG_PATTERN_ALTERNATING_USER_DEFINED", + "TPG_PATTERN_MAX" +}; + +static const char *const tpg_color_bar_mode_strings[] = { + "TPG_COLOR_BAR_MODE_INVALID", + "TPG_COLOR_BAR_MODE_NORMAL", + "TPG_COLOR_BAR_MODE_SPLIT", + "TPG_COLOR_BAR_MODE_ROTATING", + "TPG_COLOR_BAR_MODE_MAX" +}; + +static const char *const tpg_stream_type_strings[] = { + "TPG_STREAM_TYPE_INVALID", + "TPG_STREAM_TYPE_IMAGE", + "TPG_STREAM_TYPE_PDAF", + "TPG_STREAM_TYPE_META", + "TPG_STREAM_TYPE_MAX" +}; + +static const char *const tpg_image_format_type_strings[] = { + "TPG_IMAGE_FORMAT_INVALID", + "TPG_IMAGE_FORMAT_BAYER", + "TPG_IMAGE_FORMAT_QCFA", + "TPG_IMAGE_FORMAT_YUV", + "TPG_IMAGE_FORMAT_JPEG", + "TPG_IMAGE_FORMAT_MAX" +}; +#endif + +int dump_global_configs(int idx, + struct tpg_global_config_t *global) +{ +#ifdef __TPG_DEBUG_DUMP__ + CAM_DBG(CAM_TPG, "TPG[%d] phy_type : %s", + idx, + tpg_phy_type_strings[global->phy_type]); + CAM_DBG(CAM_TPG, "TPG[%d] lane_count : %d", + idx, + global->lane_count); + CAM_DBG(CAM_TPG, "TPG[%d] interleaving_format : %s", + idx, + tpg_interleaving_format_string[global->interleaving_format]); + CAM_DBG(CAM_TPG, "TPG[%d] phy_mode : %d", + idx, + global->phy_mode); + CAM_DBG(CAM_TPG, "TPG[%d] shutter_type : %s", + idx, + tpg_shutter_type_strings[global->shutter_type]); + CAM_DBG(CAM_TPG, "TPG[%d] skip pattern : 0x%x", + idx, + global->skip_pattern); + CAM_DBG(CAM_TPG, "TPG[%d] tpg clock : %d", + idx, + global->tpg_clock); +#endif + return 0; +} + +int dump_stream_configs(int hw_idx, + int stream_idx, + struct tpg_stream_config_t *stream) +{ +#ifdef __TPG_DEBUG_DUMP__ + CAM_DBG(CAM_TPG, "TPG[%d][%d] pattern_type : %s", + hw_idx, + stream_idx, + tpg_pattern_type_strings[stream->pattern_type]); + CAM_DBG(CAM_TPG, "TPG[%d][%d] cb_mode : %s", + hw_idx, + stream_idx, + tpg_color_bar_mode_strings[stream->cb_mode]); + CAM_DBG(CAM_TPG, "TPG[%d][%d] frame_count : %d", + hw_idx, + stream_idx, + stream->frame_count); + CAM_DBG(CAM_TPG, "TPG[%d][%d] stream_type : %s", + hw_idx, + stream_idx, + tpg_stream_type_strings[stream->stream_type]); + CAM_DBG(CAM_TPG, "TPG[%d][%d] left : %d", + hw_idx, + stream_idx, + stream->stream_dimension.left); + CAM_DBG(CAM_TPG, "TPG[%d][%d] top : %d", + hw_idx, + stream_idx, + stream->stream_dimension.top); + CAM_DBG(CAM_TPG, "TPG[%d][%d] width : %d", + hw_idx, + stream_idx, + stream->stream_dimension.width); + CAM_DBG(CAM_TPG, "TPG[%d][%d] height : %d", + hw_idx, + stream_idx, + stream->stream_dimension.height); + CAM_DBG(CAM_TPG, "TPG[%d][%d] pixel_depth : %d", + hw_idx, + stream_idx, + stream->pixel_depth); + CAM_DBG(CAM_TPG, "TPG[%d][%d] cfa_arrangement : %d", + hw_idx, + stream_idx, + stream->cfa_arrangement); + CAM_DBG(CAM_TPG, "TPG[%d][%d] output_format : %s", + hw_idx, + stream_idx, + tpg_image_format_type_strings[stream->output_format]); + CAM_DBG(CAM_TPG, "TPG[%d][%d] vc : 0x%x", + hw_idx, + stream_idx, + stream->vc); + CAM_DBG(CAM_TPG, "TPG[%d][%d] dt : 0x%x", + hw_idx, + stream_idx, + stream->dt); + CAM_DBG(CAM_TPG, "TPG[%d][%d] hbi : %d", + hw_idx, + stream_idx, + stream->hbi); + CAM_DBG(CAM_TPG, "TPG[%d][%d] vbi : %d", + hw_idx, + stream_idx, + stream->vbi); +#endif + return 0; +} + + +static int tpg_hw_soc_disable(struct tpg_hw *hw) +{ + int rc = 0; + + if (!hw || !hw->soc_info) { + CAM_ERR(CAM_TPG, "Error Invalid params"); + return -EINVAL; + } + + rc = cam_soc_util_disable_platform_resource(hw->soc_info, true, false); + if (rc) + CAM_ERR(CAM_TPG, "TPG[%d] Disable platform failed %d", + hw->hw_idx, rc); + + if (cam_cpas_stop(hw->cpas_handle)) { + CAM_ERR(CAM_TPG, "TPG[%d] CPAS stop failed", + hw->hw_idx); + } + + return rc; +} + +static int tpg_hw_soc_enable( + struct tpg_hw *hw, + enum cam_vote_level clk_level) +{ + int rc = 0; + struct cam_ahb_vote ahb_vote; + struct cam_axi_vote axi_vote = {0}; + + ahb_vote.type = CAM_VOTE_ABSOLUTE; + ahb_vote.vote.level = CAM_SVS_VOTE; + axi_vote.num_paths = 1; + axi_vote.axi_path[0].path_data_type = CAM_AXI_PATH_DATA_ALL; + axi_vote.axi_path[0].transac_type = CAM_AXI_TRANSACTION_WRITE; + + axi_vote.axi_path[0].camnoc_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.axi_path[0].mnoc_ab_bw = CAM_CPAS_DEFAULT_AXI_BW; + axi_vote.axi_path[0].mnoc_ib_bw = CAM_CPAS_DEFAULT_AXI_BW; + + CAM_DBG(CAM_TPG, "TPG[%d] camnoc_bw:%lld mnoc_ab_bw:%lld mnoc_ib_bw:%lld ", + hw->hw_idx, + axi_vote.axi_path[0].camnoc_bw, + axi_vote.axi_path[0].mnoc_ab_bw, + axi_vote.axi_path[0].mnoc_ib_bw); + + rc = cam_cpas_start(hw->cpas_handle, &ahb_vote, &axi_vote); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] CPAS start failed", + hw->hw_idx); + rc = -EFAULT; + goto end; + } + + rc = cam_soc_util_enable_platform_resource(hw->soc_info, true, + clk_level, false); + if (rc) { + CAM_ERR(CAM_TPG, "TPG[%d] enable platform failed", + hw->hw_idx); + goto stop_cpas; + } + + return rc; +stop_cpas: + cam_cpas_stop(hw->cpas_handle); +end: + return rc; +} + +static int tpg_hw_start_default_new(struct tpg_hw *hw) +{ + int i = 0; + uint32_t stream_idx = 0; + int num_vcs = 0; + struct global_config_args globalargs = {0}; + if (!hw || + !hw->hw_info || + !hw->hw_info->ops || + !hw->hw_info->ops->process_cmd) { + CAM_ERR(CAM_TPG, "Invalid argument"); + return -EINVAL; + } + + dump_global_configs(hw->hw_idx, &hw->global_config); + for(i = 0; i < hw->hw_info->max_vc_channels; i++) { + int dt_slot = 0; + struct vc_config_args vc_config = {0}; + struct list_head *pos = NULL, *pos_next = NULL; + struct tpg_hw_stream *entry = NULL, *vc_stream_entry = NULL; + + if (hw->vc_slots[i].vc == -1) + break; + num_vcs++; + vc_config.vc_slot = i; + vc_config.num_dts = hw->vc_slots[i].stream_count; + vc_stream_entry = list_first_entry(&hw->vc_slots[i].head, + struct tpg_hw_stream, list); + vc_config.stream = &vc_stream_entry->stream; + hw->hw_info->ops->process_cmd(hw, + TPG_CONFIG_VC, &vc_config); + + list_for_each_safe(pos, pos_next, &hw->vc_slots[i].head) { + struct dt_config_args dt_config = {0}; + entry = list_entry(pos, struct tpg_hw_stream, list); + dump_stream_configs(hw->hw_idx, + stream_idx++, + &entry->stream); + dt_config.vc_slot = i; + dt_config.dt_slot = dt_slot++; + dt_config.stream = &entry->stream; + hw->hw_info->ops->process_cmd(hw, TPG_CONFIG_DT, &dt_config); + } + } + + globalargs.num_vcs = num_vcs; + globalargs.globalconfig = &hw->global_config; + hw->hw_info->ops->process_cmd(hw, + TPG_CONFIG_CTRL, &globalargs); + + return 0; +} + +int tpg_hw_dump_status(struct tpg_hw *hw) +{ + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_3: + if (hw->hw_info->ops->dump_status) + hw->hw_info->ops->dump_status(hw, NULL); + default: + CAM_WARN(CAM_TPG, "Hw version doesn't support status dump"); + break; + } + return 0; +} + +int tpg_hw_start(struct tpg_hw *hw) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + mutex_lock(&hw->mutex); + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_0: + case TPG_HW_VERSION_1_1: + if (hw->hw_info->ops->start) + hw->hw_info->ops->start(hw, NULL); + break; + case TPG_HW_VERSION_1_3: + if (hw->hw_info->ops->start) + hw->hw_info->ops->start(hw, NULL); + tpg_hw_start_default_new(hw); + cam_tpg_mem_dmp(hw->soc_info); + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported HW Version", + hw->hw_idx); + rc = -EINVAL; + break; + } + mutex_unlock(&hw->mutex); + return rc; +} + +int tpg_hw_stop(struct tpg_hw *hw) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + mutex_lock(&hw->mutex); + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_0: + case TPG_HW_VERSION_1_1: + case TPG_HW_VERSION_1_3: + if (hw->hw_info->ops->stop) + rc = hw->hw_info->ops->stop(hw, NULL); + rc = tpg_hw_soc_disable(hw); + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported HW Version", + hw->hw_idx); + rc = -EINVAL; + break; + } + mutex_unlock(&hw->mutex); + + return rc; +} + +int tpg_hw_acquire(struct tpg_hw *hw, + struct tpg_hw_acquire_args *acquire) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + + mutex_lock(&hw->mutex); + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_0: + case TPG_HW_VERSION_1_1: + case TPG_HW_VERSION_1_3: + // Start Cpas and enable required clocks + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported HW Version", + hw->hw_idx); + rc = -EINVAL; + break; + } + mutex_unlock(&hw->mutex); + return rc; +} + +int tpg_hw_release(struct tpg_hw *hw) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + mutex_lock(&hw->mutex); + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_0: + case TPG_HW_VERSION_1_1: + case TPG_HW_VERSION_1_3: + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported HW Version", + hw->hw_idx); + rc = -EINVAL; + break; + } + mutex_unlock(&hw->mutex); + return rc; +} + +static int tpg_hw_configure_init_settings( + struct tpg_hw *hw, + struct tpg_hw_initsettings *settings) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + mutex_lock(&hw->mutex); + switch (hw->hw_info->version) { + case TPG_HW_VERSION_1_0: + case TPG_HW_VERSION_1_1: + case TPG_HW_VERSION_1_3: + rc = tpg_hw_soc_enable(hw, CAM_SVS_VOTE); + if (hw->hw_info->ops->init) + rc = hw->hw_info->ops->init(hw, settings); + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported HW Version", + hw->hw_idx); + rc = -EINVAL; + break; + } + mutex_unlock(&hw->mutex); + return rc; +} + +int tpg_hw_config( + struct tpg_hw *hw, + enum tpg_hw_cmd_t config_cmd, + void *config_args) +{ + int rc = 0; + + if (!hw || !hw->hw_info || !hw->hw_info->ops) + return -EINVAL; + switch (config_cmd) { + case TPG_HW_CMD_INIT_CONFIG: + //validate_stream_list(hw); + tpg_hw_configure_init_settings(hw, + (struct tpg_hw_initsettings *)config_args); + break; + default: + CAM_ERR(CAM_TPG, "TPG[%d] Unsupported hw config command", + hw->hw_idx); + rc = -EINVAL; + break; + } + return rc; +} + +int tpg_hw_free_streams(struct tpg_hw *hw) +{ + struct list_head *pos = NULL, *pos_next = NULL; + struct tpg_hw_stream *entry; + int i = 0; + + if (!hw) + return -EINVAL; + + mutex_lock(&hw->mutex); + /* free up the streams */ + CAM_DBG(CAM_TPG, "TPG[%d] Freeing all the streams", hw->hw_idx); + + /* reset the slots */ + for(i = 0; i < hw->hw_info->max_vc_channels; i++) { + hw->vc_slots[i].slot_id = i; + hw->vc_slots[i].vc = -1; + hw->vc_slots[i].stream_count = 0; + list_for_each_safe(pos, pos_next, &hw->vc_slots[i].head) { + entry = list_entry(pos, struct tpg_hw_stream, list); + list_del(pos); + kfree(entry); + } + INIT_LIST_HEAD(&(hw->vc_slots[i].head)); + } + hw->vc_count = 0; + + mutex_unlock(&hw->mutex); + return 0; +} + +int tpg_hw_copy_global_config( + struct tpg_hw *hw, + struct tpg_global_config_t *global) +{ + if (!hw || !global) { + CAM_ERR(CAM_TPG, "invalid parameter"); + return -EINVAL; + } + + mutex_lock(&hw->mutex); + memcpy(&hw->global_config, + global, + sizeof(struct tpg_global_config_t)); + mutex_unlock(&hw->mutex); + return 0; +} + +static int assign_vc_slot( + struct tpg_hw *hw, + int vc, + struct tpg_hw_stream *stream + ) +{ + int rc = -EINVAL, i = 0, slot_matched = 0; + + if (!hw || !stream) { + return -EINVAL; + } + + for(i = 0; i < hw->hw_info->max_vc_channels; i++) { + /* Found a matching slot */ + if(hw->vc_slots[i].vc == vc) { + slot_matched = 1; + if (hw->vc_slots[i].stream_count + < hw->hw_info->max_dt_channels_per_vc) { + list_add_tail(&stream->list, &hw->vc_slots[i].head); + hw->vc_slots[i].stream_count++; + hw->vc_slots[i].vc = vc; + rc = 0; + CAM_DBG(CAM_TPG, "vc[%d]dt[%d]=>slot[%d]", vc, stream->stream.dt, i); + break; + } else { + + /** + * already slot was assigned for this vc + * however this slot have been filled with + * full streams + */ + rc = -EINVAL; + CAM_ERR(CAM_TPG, "vc[%d]dt[%d]=>slot[%d] is overlfown", + vc, stream->stream.dt, i); + break; + } + } + + /** + * none of the above slots matched, and now found an empty slot + * so assigning stream to that slot + */ + if (hw->vc_slots[i].vc == -1) { + list_add_tail(&stream->list, &hw->vc_slots[i].head); + hw->vc_slots[i].stream_count++; + hw->vc_slots[i].vc = vc; + hw->vc_count++; + rc = 0; + CAM_DBG(CAM_TPG, "vc[%d]dt[%d]=>slot[%d]", vc, stream->stream.dt, i); + break; + } + } + if ((slot_matched == 0) && (rc != 0)) { + CAM_ERR(CAM_TPG, "No slot matched"); + } + return rc; +} + +int tpg_hw_reset(struct tpg_hw *hw) +{ + int rc = 0; + if (!hw) + return -EINVAL; + + /* free up the streams if any*/ + rc = tpg_hw_free_streams(hw); + if (rc) + CAM_ERR(CAM_TPG, "TPG[%d] Unable to free up the streams", hw->hw_idx); + + /* disable the hw */ + if (cam_cpas_stop(hw->cpas_handle)) { + CAM_ERR(CAM_TPG, "TPG[%d] CPAS stop failed", + hw->hw_idx); + } + + return rc; +} + +int tpg_hw_add_stream( + struct tpg_hw *hw, + struct tpg_stream_config_t *cmd) +{ + int rc = 0; + struct tpg_hw_stream *stream = NULL; + if (!hw || !cmd) { + CAM_ERR(CAM_TPG, "Invalid params"); + return -EINVAL; + } + + mutex_lock(&hw->mutex); + stream = kzalloc(sizeof(struct tpg_hw_stream), GFP_KERNEL); + if (!stream) { + CAM_ERR(CAM_TPG, "TPG[%d] stream allocation failed", + hw->hw_idx); + mutex_unlock(&hw->mutex); + return -ENOMEM; + } + memcpy(&stream->stream, + cmd, + sizeof(struct tpg_stream_config_t)); + + rc = assign_vc_slot(hw, stream->stream.vc, stream); + mutex_unlock(&hw->mutex); + return rc; +} diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.h b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.h new file mode 100644 index 0000000000..96273c09c8 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __TPG_HW_H__ +#define __TPG_HW_H__ + +#include +#include "cam_debug_util.h" +#include "cam_soc_util.h" +#include +#include +#define TPG_HW_VERSION_1_0 0x10000000 +#define TPG_HW_VERSION_1_1 0x10000001 +#define TPG_HW_VERSION_1_3 0x10000003 + + +struct tpg_hw; + +/** + * tpg_hw_ops : tpg hw operations + * + * @init : tpg hw layer init + * @start : tpg hw start stream + * @stop : tpg hw stop stream + * @deinit : tpg hw deinit + * @read : tpg hw read register + * @write : tpg hw write register + * @process_cmd : tpg hw process command + * @dump_status : dump any status registers + */ +struct tpg_hw_ops { + int (*init)(struct tpg_hw *hw, void *data); + int (*start)(struct tpg_hw *hw, void *data); + int (*stop)(struct tpg_hw *hw, void *data); + int (*deinit)(struct tpg_hw *hw, void *data); + int (*read)(struct tpg_hw *hw, uint32_t *addr, uint32_t *val); + int (*write)(struct tpg_hw *hw, uint32_t *addr, uint32_t *val); + int (*process_cmd)(struct tpg_hw *hw, uint32_t cmd, void *arg); + int (*dump_status)(struct tpg_hw *hw, void *data); +}; + +/** + * @brief tpg_vc_slot_info + * @slot_id : slot id of this vc slot + * @vc : virtual channel configured + * @stream_count : number of streams in this slot + * @head : head pointing all data types in with this vc + */ +struct tpg_vc_slot_info { + int slot_id; + int vc; + int stream_count; + struct list_head head; +}; + +/** + * tpg_hw_info : tpg hw layer info + * + * @version: version of tpg hw + * @max_vc_channels: max number of virtual channels supported by tpg + * @max_dt_channels_per_vc: max dts supported in each vc + * @ops: tpg hw operations + */ +struct tpg_hw_info { + uint32_t version; + uint32_t max_vc_channels; + uint32_t max_dt_channels_per_vc; + struct tpg_hw_ops *ops; +}; + + +/** + * tpg_hw_stream : tpg hw stream + * + * @stream : tpg stream; + * @list : entry to tpg stream list + */ +struct tpg_hw_stream { + struct tpg_stream_config_t stream; + struct list_head list; +}; + +/** + * tpg_hw : tpg hw + * + * @hw_idx : hw id + * @state : tpg hw state + * @cpas_handle : handle to cpas + * @hw_info : tp hw info + * @soc_info : soc info + * @mutex : lock + * @stream_list : list of tpg stream + * @global_config : global configuration + */ +struct tpg_hw { + uint32_t hw_idx; + uint32_t state; + uint32_t cpas_handle; + uint32_t vc_count; + struct tpg_hw_info *hw_info; + struct cam_hw_soc_info *soc_info; + struct mutex mutex; + struct tpg_vc_slot_info *vc_slots; + struct tpg_global_config_t global_config; +}; + +/** + * tpg_hw_acquire_args : tpg hw acquire arguments + * + * @resource_list : list of resources to acquire + * @count : number of resources to acquire + */ +struct tpg_hw_acquire_args { + /* Integer id of resources */ + uint32_t *resource_list; + ssize_t count; +}; + +enum tpg_hw_cmd_t { + TPG_HW_CMD_INVALID = 0, + TPG_HW_CMD_INIT_CONFIG, + TPG_HW_CMD_MAX, +}; + +#define TPG_HW_CONFIG_BASE 0x4000 +#define TPG_CONFIG_CTRL (TPG_HW_CONFIG_BASE + 0) +#define TPG_CONFIG_VC (TPG_HW_CONFIG_BASE + 1) +#define TPG_CONFIG_DT (TPG_HW_CONFIG_BASE + 2) + +/* pixel bit width */ +#define PACK_8_BIT 8 +#define PACK_10_BIT 10 +#define PACK_12_BIT 12 +#define PACK_14_BIT 14 +#define PACK_16_BIT 16 + +/** + * @vc_config_args : arguments for vc config process cmd + * + * @vc_slot : slot to configure this vc + * @num_dts : number of dts in this vc + * @stream : output stream + */ +struct vc_config_args { + uint32_t vc_slot; + uint32_t num_dts; + struct tpg_stream_config_t *stream; +}; + +/** + * dt_config_args : arguments for dt config process cmd + * + * @vc_slot : vc slot to configure this dt + * @dt_slot : dt slot to configure this dt + * @stream : stream packet to configure for this dt + */ +struct dt_config_args { + uint32_t vc_slot; + uint32_t dt_slot; + struct tpg_stream_config_t *stream; +}; + +/** + * global_config_args : tpg global config args + * + * @num_vcs : number of vcs to be configured + * @globalconfig: global config cmd + */ +struct global_config_args { + uint32_t num_vcs; + struct tpg_global_config_t *globalconfig; +}; + +/** + * tpg_hw_initsettings : initial configurations + * + * @global_config : global configuration + * @streamconfigs : array of stream configurations + * @num_streams : number of streams in strea config array + */ +struct tpg_hw_initsettings { + struct tpg_global_config_t globalconfig; + struct tpg_stream_config_t *streamconfigs; + uint32_t num_streams; +}; + +/** + * @brief dump the tpg memory info + * + * @param soc_info: soc info for soc related info + * + * @return : 0 on succuss + */ +int32_t cam_tpg_mem_dmp(struct cam_hw_soc_info *soc_info); + +/** + * @brief dump global config command + * + * @param idx : hw index + * @param global : global config command + * + * @return : 0 on success + */ +int dump_global_configs(int idx, struct tpg_global_config_t *global); + +/** + * @brief : dump stream config command + * + * @param hw_idx: hw index + * @param stream_idx: stream index + * @param stream: stream config command + * + * @return : 0 on success + */ +int dump_stream_configs(int hw_idx, int stream_idx, struct tpg_stream_config_t *stream); + +/** + * @brief : dump any hw status registers + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_dump_status(struct tpg_hw *hw); +/** + * @brief : start tpg hw stream + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_start(struct tpg_hw *hw); + +/** + * @brief : stop tpg hw stream + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_stop(struct tpg_hw *hw); + +/** + * @brief : tpg hw acquire + * + * @param hw: tpg hw instance + * @param acquire: list of resources to acquire + * + * @return : 0 on success + */ +int tpg_hw_acquire(struct tpg_hw *hw, + struct tpg_hw_acquire_args *acquire); +/** + * @brief release tpg hw + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_release(struct tpg_hw *hw); + +/** + * @brief : configure tpg hw + * + * @param hw: tpg hw instance + * @param cmd: configuration command + * @param arg: configuration command argument + * + * @return : 0 on success + */ +int tpg_hw_config(struct tpg_hw *hw, enum tpg_hw_cmd_t cmd, void *arg); + +/** + * @brief : tpg free streams + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_free_streams(struct tpg_hw *hw); + +/** + * @brief : reset the tpg hw instance + * + * @param hw: tpg hw instance + * + * @return : 0 on success + */ +int tpg_hw_reset(struct tpg_hw *hw); + +/** + * @brief : tp hw add stream + * + * @param hw: tpg hw instance + * @param cmd: tpg hw command + * + * @return : 0 on success + */ +int tpg_hw_add_stream(struct tpg_hw *hw, struct tpg_stream_config_t *cmd); + +/** + * @brief : copy global config command + * + * @param hw: tpg hw instance + * @param global: global config command + * + * @return : 0 on success + */ +int tpg_hw_copy_global_config(struct tpg_hw *hw, struct tpg_global_config_t *global); + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.c b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.c new file mode 100644 index 0000000000..a8b76ff8c9 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#include "tpg_hw_v_1_0.h" + +enum tpg_hw_encode_format_t { + RAW_8_BIT = 1, + RAW_10_BIT, + RAW_12_BIT, + RAW_14_BIT, + RAW_16_BIT +}; + +static struct cam_tpg_ver1_reg_offset cam_tpg101_reg = { + .tpg_hw_version = 0x0, + .tpg_hw_status = 0x4, + .tpg_ctrl = 0x60, + .tpg_vc_cfg0 = 0x64, + .tpg_vc_cfg1 = 0x68, + .tpg_lfsr_seed = 0x6c, + .tpg_dt_0_cfg_0 = 0x70, + .tpg_dt_1_cfg_0 = 0x74, + .tpg_dt_2_cfg_0 = 0x78, + .tpg_dt_3_cfg_0 = 0x7C, + .tpg_dt_0_cfg_1 = 0x80, + .tpg_dt_1_cfg_1 = 0x84, + .tpg_dt_2_cfg_1 = 0x88, + .tpg_dt_3_cfg_1 = 0x8C, + .tpg_dt_0_cfg_2 = 0x90, + .tpg_dt_1_cfg_2 = 0x94, + .tpg_dt_2_cfg_2 = 0x98, + .tpg_dt_3_cfg_2 = 0x9C, + .tpg_color_bar_cfg = 0xA0, + .tpg_common_gen_cfg = 0xA4, + .tpg_vbi_cfg = 0xA8, + .tpg_test_bus_crtl = 0xF8, + .tpg_spare = 0xFC, + + /* configurations */ + .major_version = 1, + .minor_version = 0, + .version_incr = 0, + .tpg_en_shift_val = 0, + .tpg_phy_sel_shift_val = 3, + .tpg_num_active_lines_shift = 4, + .tpg_fe_pkt_en_shift = 2, + .tpg_fs_pkt_en_shift = 1, + .tpg_line_interleaving_mode_shift = 10, + .tpg_num_dts_shift_val = 8, + .tpg_v_blank_cnt_shift = 12, + .tpg_dt_encode_format_shift = 16, + .tpg_payload_mode_color = 0x8, + .tpg_split_en_shift = 5, + .top_mux_reg_offset = 0x1C, +}; + +static int configure_global_configs(struct tpg_hw *hw, + struct tpg_global_config_t *configs) +{ + uint32_t val; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver1_reg_offset *tpg_reg = &cam_tpg101_reg; + + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + soc_info = hw->soc_info; + + val = cam_io_r_mb(soc_info->reg_map[1].mem_base + + tpg_reg->top_mux_reg_offset); + val |= (1 << hw->hw_idx); + + cam_io_w_mb(val, + soc_info->reg_map[1].mem_base + tpg_reg->top_mux_reg_offset); + CAM_INFO(CAM_ISP, "TPG:%d Set top Mux: 0x%x", + hw->hw_idx, val); + + val = ((4 - 1) << + tpg_reg->tpg_num_active_lines_shift) | + (1 << tpg_reg->tpg_fe_pkt_en_shift) | + (1 << tpg_reg->tpg_fs_pkt_en_shift) | + (0 << tpg_reg->tpg_phy_sel_shift_val) | + (1 << tpg_reg->tpg_en_shift_val); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + tpg_reg->tpg_ctrl); + + return 0; +} + +static int get_tpg_encode_format(int sw_encode_format) +{ + switch (sw_encode_format) { + case PACK_8_BIT: + return RAW_8_BIT; + case PACK_10_BIT: + return RAW_10_BIT; + case PACK_12_BIT: + return RAW_12_BIT; + case PACK_14_BIT: + return RAW_14_BIT; + case PACK_16_BIT: + return RAW_16_BIT; + } + return RAW_8_BIT; +} + +static int configure_dt( + struct tpg_hw *hw, + uint32_t vc_slot, + uint32_t dt_slot, + struct tpg_stream_config_t *stream) +{ + uint32_t val; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver1_reg_offset *tpg_reg = &cam_tpg101_reg; + + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + soc_info = hw->soc_info; + + CAM_DBG(CAM_TPG, "TPG[%d] slot(%d,%d) <= dt:%d", + hw->hw_idx, + vc_slot, + dt_slot, + stream->dt); + /* configure width and height */ + val = (((stream->stream_dimension.width & 0xFFFF) << 16) | + (stream->stream_dimension.height & 0x3FFF)); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_dt_0_cfg_0 + 0x10 * dt_slot); + + /* configure data type */ + cam_io_w_mb(stream->dt, + soc_info->reg_map[0].mem_base + + tpg_reg->tpg_dt_0_cfg_1 + 0x10 * dt_slot); + + /* configure bpp */ + val = ((get_tpg_encode_format(stream->pixel_depth) & 0xF) << + tpg_reg->tpg_dt_encode_format_shift) | + tpg_reg->tpg_payload_mode_color; + + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_dt_0_cfg_2 + 0x10 * dt_slot); + + return 0; +} + +static int configure_vc( + struct tpg_hw *hw, + uint32_t vc_slot, + int num_dts, + struct tpg_stream_config_t *stream) +{ + uint32_t val = 0; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver1_reg_offset *tpg_reg = &cam_tpg101_reg; + + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + soc_info = hw->soc_info; + + CAM_DBG(CAM_TPG, "Configureing vc : %d at the slot : %d num_dts=%d", + stream->vc, vc_slot, num_dts); + val = ((num_dts - 1) << + tpg_reg->tpg_num_dts_shift_val) | stream->vc; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + tpg_reg->tpg_vc_cfg0); + + cam_io_w_mb(stream->hbi, + soc_info->reg_map[0].mem_base + tpg_reg->tpg_vc_cfg1); + + val = (1 << tpg_reg->tpg_split_en_shift); + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_common_gen_cfg); + + cam_io_w_mb(stream->vbi, + soc_info->reg_map[0].mem_base + tpg_reg->tpg_vbi_cfg); + + return 0; +} + +static int tpg_hw_v_1_0_reset(struct tpg_hw *hw, void *data) +{ + struct cam_hw_soc_info *soc_info = NULL; + uint32_t val; + struct cam_tpg_ver1_reg_offset *tpg_reg = &cam_tpg101_reg; + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + soc_info = hw->soc_info; + + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_ctrl); + + /* Reset the TOP tpg mux sel*/ + val = cam_io_r_mb(soc_info->reg_map[1].mem_base + + tpg_reg->top_mux_reg_offset); + val &= ~(1 << hw->hw_idx); + + cam_io_w_mb(val, + soc_info->reg_map[1].mem_base + tpg_reg->top_mux_reg_offset); + CAM_INFO(CAM_TPG, "TPG:%d Reset Top Mux: 0x%x", + hw->hw_idx, val); + + return 0; +} + +int tpg_hw_v_1_0_process_cmd( + struct tpg_hw *hw, + uint32_t cmd, + void *arg) +{ + + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + switch(cmd) { + case TPG_CONFIG_VC: + { + struct vc_config_args *vc_config = + (struct vc_config_args *)arg; + + if (vc_config == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + configure_vc(hw, + vc_config->vc_slot, + vc_config->num_dts, + vc_config->stream); + } + break; + case TPG_CONFIG_DT: + { + struct dt_config_args *dt_config = + (struct dt_config_args *)arg; + + if (dt_config == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + configure_dt(hw, + dt_config->vc_slot, + dt_config->dt_slot, + dt_config->stream); + } + break; + case TPG_CONFIG_CTRL: + configure_global_configs(hw, arg); + break; + default: + CAM_ERR(CAM_TPG, "invalid argument"); + break; + } + return 0; +} + +int tpg_hw_v_1_0_stop(struct tpg_hw *hw, void *data) +{ + CAM_INFO(CAM_TPG, "TPG V1.0 HWL stop"); + tpg_hw_v_1_0_reset(hw, data); + return 0; +} + +int tpg_hw_v_1_0_start(struct tpg_hw *hw, void *data) +{ + CAM_DBG(CAM_TPG, "TPG V1.3 HWL start"); + return 0; +} + +int tpg_hw_v_1_0_init(struct tpg_hw *hw, void *data) +{ + CAM_INFO(CAM_TPG, "TPG V1.0 HWL init"); + tpg_hw_v_1_0_reset(hw, data); + return 0; +} diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.h b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.h new file mode 100644 index 0000000000..1f6d2cec9a --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __TPG_HW_V_1_0_H__ +#define __TPG_HW_V_1_0_H__ + +#include "../tpg_hw.h" + +struct cam_tpg_ver1_reg_offset { + uint32_t tpg_hw_version; + uint32_t tpg_hw_status; + uint32_t tpg_ctrl; + uint32_t tpg_vc_cfg0; + uint32_t tpg_vc_cfg1; + uint32_t tpg_lfsr_seed; + uint32_t tpg_dt_0_cfg_0; + uint32_t tpg_dt_1_cfg_0; + uint32_t tpg_dt_2_cfg_0; + uint32_t tpg_dt_3_cfg_0; + uint32_t tpg_dt_0_cfg_1; + uint32_t tpg_dt_1_cfg_1; + uint32_t tpg_dt_2_cfg_1; + uint32_t tpg_dt_3_cfg_1; + uint32_t tpg_dt_0_cfg_2; + uint32_t tpg_dt_1_cfg_2; + uint32_t tpg_dt_2_cfg_2; + uint32_t tpg_dt_3_cfg_2; + uint32_t tpg_color_bar_cfg; + uint32_t tpg_common_gen_cfg; + uint32_t tpg_vbi_cfg; + uint32_t tpg_test_bus_crtl; + uint32_t tpg_spare; + + /* configurations */ + uint32_t major_version; + uint32_t minor_version; + uint32_t version_incr; + uint32_t tpg_en_shift_val; + uint32_t tpg_phy_sel_shift_val; + uint32_t tpg_num_active_lines_shift; + uint32_t tpg_fe_pkt_en_shift; + uint32_t tpg_fs_pkt_en_shift; + uint32_t tpg_line_interleaving_mode_shift; + uint32_t tpg_num_dts_shift_val; + uint32_t tpg_v_blank_cnt_shift; + uint32_t tpg_dt_encode_format_shift; + uint32_t tpg_payload_mode_color; + uint32_t tpg_split_en_shift; + uint32_t top_mux_reg_offset; +}; + +/** + * @brief : initialize the tpg hw v 1.0 + * + * @param hw: tpg hw instance + * @param data: initialize data + * + * @return : return 0 on success + */ +int tpg_hw_v_1_0_init(struct tpg_hw *hw, void *data); + +/** + * @brief : start tpg hw v 1.0 + * + * @param hw: tpg hw instance + * @param data: start argument + * + * @return : 0 on success + */ +int tpg_hw_v_1_0_start(struct tpg_hw *hw, void *data); + +/** + * @brief : stop tpg hw + * + * @param hw: tpg hw instance + * @param data: arguments to stop tpg hw 1.0 + * + * @return : 0 on success + */ +int tpg_hw_v_1_0_stop(struct tpg_hw *hw, void *data); + + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h new file mode 100644 index 0000000000..e1a374f8af --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_0/tpg_hw_v_1_0_data.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __TPG_HW_V_1_0_DATA_H__ +#define __TPG_HW_V_1_0_DATA_H__ + +#include "../tpg_hw.h" +#include "tpg_hw_v_1_0.h" + +struct tpg_hw_ops tpg_hw_v_1_0_ops = { + .start = tpg_hw_v_1_0_start, + .stop = tpg_hw_v_1_0_stop, + .init = tpg_hw_v_1_0_init, +}; + +struct tpg_hw_info tpg_v_1_0_hw_info = { + .version = TPG_HW_VERSION_1_0, + .max_vc_channels = 2, + .max_dt_channels_per_vc = 4, + .ops = &tpg_hw_v_1_0_ops, +}; + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.c b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.c new file mode 100644 index 0000000000..f0eb957938 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#include "tpg_hw_v_1_3.h" + +enum tpg_hw_v_1_3_encode_fomat_t { + RAW_8_BIT = 1, + RAW_10_BIT, + RAW_12_BIT, + RAW_14_BIT, + RAW_16_BIT +}; + +static struct cam_tpg_ver_1_3_reg_offset cam_tpg103_reg = { + .tpg_hw_version = 0x0, + .tpg_hw_status = 0x4, + .tpg_ctrl = 0x64, + .tpg_vc0_cfg0 = 0x68, + .tpg_vc0_lfsr_seed = 0x6C, + .tpg_vc0_hbi_cfg = 0x70, + .tpg_vc0_vbi_cfg = 0x74, + .tpg_vc0_color_bar_cfg = 0x78, + .tpg_vc0_dt_0_cfg_0 = 0x7C, + .tpg_vc0_dt_0_cfg_1 = 0x80, + .tpg_vc0_dt_0_cfg_2 = 0x84, + .tpg_vc0_dt_1_cfg_0 = 0x88, + .tpg_vc0_dt_1_cfg_1 = 0x8C, + .tpg_vc0_dt_1_cfg_2 = 0x90, + .tpg_vc0_dt_2_cfg_0 = 0x94, + .tpg_vc0_dt_2_cfg_1 = 0x98, + .tpg_vc0_dt_2_cfg_2 = 0x9C, + .tpg_vc0_dt_3_cfg_0 = 0xA0, + .tpg_vc0_dt_3_cfg_1 = 0xA4, + .tpg_vc0_dt_3_cfg_2 = 0xA8, + + .tpg_vc1_cfg0 = 0xC8, + .tpg_vc1_lfsr_seed = 0xCC, + .tpg_vc1_hbi_cfg = 0xD0, + .tpg_vc1_vbi_cfg = 0xD4, + .tpg_vc1_color_bar_cfg = 0xD8, + .tpg_vc1_dt_0_cfg_0 = 0xDC, + .tpg_vc1_dt_0_cfg_1 = 0xE0, + .tpg_vc1_dt_0_cfg_2 = 0xE4, + .tpg_vc1_dt_1_cfg_0 = 0xE8, + .tpg_vc1_dt_1_cfg_1 = 0xEC, + .tpg_vc1_dt_1_cfg_2 = 0xF0, + .tpg_vc1_dt_2_cfg_0 = 0xF4, + .tpg_vc1_dt_2_cfg_1 = 0xF8, + .tpg_vc1_dt_2_cfg_2 = 0xFC, + .tpg_vc1_dt_3_cfg_0 = 0x100, + .tpg_vc1_dt_3_cfg_1 = 0x104, + .tpg_vc1_dt_3_cfg_2 = 0x108, + + .tpg_vc2_cfg0 = 0x128, + .tpg_vc2_lfsr_seed = 0x12C, + .tpg_vc2_hbi_cfg = 0x130, + .tpg_vc2_vbi_cfg = 0x134, + .tpg_vc2_color_bar_cfg = 0x138, + .tpg_vc2_dt_0_cfg_0 = 0x13C, + .tpg_vc2_dt_0_cfg_1 = 0x140, + .tpg_vc2_dt_0_cfg_2 = 0x144, + .tpg_vc2_dt_1_cfg_0 = 0x148, + .tpg_vc2_dt_1_cfg_1 = 0x14C, + .tpg_vc2_dt_1_cfg_2 = 0x150, + .tpg_vc2_dt_2_cfg_0 = 0x154, + .tpg_vc2_dt_2_cfg_1 = 0x158, + .tpg_vc2_dt_2_cfg_2 = 0x15C, + .tpg_vc2_dt_3_cfg_0 = 0x160, + .tpg_vc2_dt_3_cfg_1 = 0x164, + .tpg_vc2_dt_3_cfg_2 = 0x168, + + .tpg_vc3_cfg0 = 0x188, + .tpg_vc3_lfsr_seed = 0x18C, + .tpg_vc3_hbi_cfg = 0x190, + .tpg_vc3_vbi_cfg = 0x194, + .tpg_vc3_color_bar_cfg = 0x198, + .tpg_vc3_dt_0_cfg_0 = 0x19C, + .tpg_vc3_dt_0_cfg_1 = 0x1A0, + .tpg_vc3_dt_0_cfg_2 = 0x1A4, + .tpg_vc3_dt_1_cfg_0 = 0x1A8, + .tpg_vc3_dt_1_cfg_1 = 0x1AC, + .tpg_vc3_dt_1_cfg_2 = 0x1B0, + .tpg_vc3_dt_2_cfg_0 = 0x1B4, + .tpg_vc3_dt_2_cfg_1 = 0x1B8, + .tpg_vc3_dt_2_cfg_2 = 0x1BC, + .tpg_vc3_dt_3_cfg_0 = 0x1C0, + .tpg_vc3_dt_3_cfg_1 = 0x1C4, + .tpg_vc3_dt_3_cfg_2 = 0x1C8, + .tpg_throttle = 0x1CC, + .tpg_top_irq_status = 0x1E0, + .tpg_top_irq_mask = 0x1E4, + .tpg_top_irq_clear = 0x1E8, + .tpg_top_irq_set = 0x1EC, + .tpg_top_irq_cmd = 0x1F0, + .tpg_top_clear = 0x1F4, + .tpg_test_bus_crtl = 0x1F8, + .tpg_spare = 0x1FC, + + /* configurations */ + .major_version = 2, + .minor_version = 0, + .version_incr = 0, + .tpg_en_shift_val = 0, + .tpg_cphy_dphy_sel_shift_val = 3, + .tpg_num_active_lanes_shift = 4, + .tpg_fe_pkt_en_shift = 2, + .tpg_fs_pkt_en_shift = 1, + .tpg_line_interleaving_mode_shift = 10, + .tpg_num_frames_shift_val = 16, + .tpg_num_dts_shift_val = 8, + .tpg_v_blank_cnt_shift = 12, + .tpg_dt_encode_format_shift = 20, + .tpg_payload_mode_color = 0x8, + .tpg_split_en_shift = 4, + .top_mux_reg_offset = 0x1C, + .tpg_vc_dt_pattern_id_shift = 6, + .tpg_num_active_vcs_shift = 30, + .tpg_color_bar_qcfa_en_shift = 3, + .tpg_color_bar_qcfa_rotate_period_shift = 8, +}; + +#define FRAME_INTERLEAVE 0x0 +#define LINE_INTERLEAVE 0x1 +#define SHDR_INTERLEAVE 0x2 +#define SPARSE_PD_INTERLEAVE 0x3 +static int get_tpg_vc_dt_pattern_id( + enum tpg_interleaving_format_t vc_dt_pattern) +{ + switch (vc_dt_pattern) { + case TPG_INTERLEAVING_FORMAT_INVALID: + case TPG_INTERLEAVING_FORMAT_MAX: + case TPG_INTERLEAVING_FORMAT_FRAME: + return FRAME_INTERLEAVE; + case TPG_INTERLEAVING_FORMAT_LINE: + return LINE_INTERLEAVE; + case TPG_INTERLEAVING_FORMAT_SHDR: + return SHDR_INTERLEAVE; + case TPG_INTERLEAVING_FORMAT_SPARSE_PD: + return SPARSE_PD_INTERLEAVE; + + } + return FRAME_INTERLEAVE; +} + +static int configure_global_configs( + struct tpg_hw *hw, + int num_vcs, + struct tpg_global_config_t *configs) +{ + uint32_t val, phy_type = 0; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver_1_3_reg_offset *tpg_reg = &cam_tpg103_reg; + + if (!hw) { + CAM_ERR(CAM_TPG, "invalid params"); + return -EINVAL; + } + soc_info = hw->soc_info; + + if (configs->phy_type == TPG_PHY_TYPE_CPHY) + phy_type = 1; + + if (num_vcs <= 0) { + CAM_ERR(CAM_TPG, "Invalid vc count"); + return -EINVAL; + } + + val = configs->skip_pattern; + cam_io_w_mb(val, + soc_info->reg_map[0].mem_base + tpg_reg->tpg_throttle); + CAM_DBG(CAM_TPG, "tpg[%d] throttle=0x%x", hw->hw_idx, val); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_irq_mask); + + + val = ((num_vcs - 1) << + (tpg_reg->tpg_num_active_vcs_shift) | + (configs->lane_count - 1) << + tpg_reg->tpg_num_active_lanes_shift) | + get_tpg_vc_dt_pattern_id(configs->interleaving_format) << + (tpg_reg->tpg_vc_dt_pattern_id_shift) | + (phy_type << tpg_reg->tpg_cphy_dphy_sel_shift_val) | + (1 << tpg_reg->tpg_en_shift_val); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + tpg_reg->tpg_ctrl); + CAM_DBG(CAM_TPG, "tpg[%d] tpg_ctrl=0x%x", hw->hw_idx, val); + + return 0; +} + +static int get_tpg_encode_format(int sw_encode_format) +{ + switch (sw_encode_format) { + case PACK_8_BIT: + return RAW_8_BIT; + case PACK_10_BIT: + return RAW_10_BIT; + case PACK_12_BIT: + return RAW_12_BIT; + case PACK_14_BIT: + return RAW_14_BIT; + case PACK_16_BIT: + return RAW_16_BIT; + } + return RAW_8_BIT; +} + +#define INCREMENTING 0x0 +#define ALTERNATING_55_AA 0x1 +#define RANDOM 0x4 +#define USER_SPECIFIED 0x5 +#define COLOR_BARS 0x8 + +static int get_tpg_payload_mode(enum tpg_pattern_t pattern) +{ + switch (pattern) { + case TPG_PATTERN_INVALID: + case TPG_PATTERN_REAL_IMAGE: + case TPG_PATTERN_COLOR_BAR: + return COLOR_BARS; + case TPG_PATTERN_RANDOM_PIXL: + case TPG_PATTERN_RANDOM_INCREMENTING_PIXEL: + return RANDOM; + case TPG_PATTERN_ALTERNATING_55_AA: + return ALTERNATING_55_AA; + case TPG_PATTERN_ALTERNATING_USER_DEFINED: + return USER_SPECIFIED; + default: + return COLOR_BARS; + } + return COLOR_BARS; +} + +static int configure_dt( + struct tpg_hw *hw, + uint32_t vc_slot, + uint32_t dt_slot, + struct tpg_stream_config_t *stream) +{ + uint32_t val; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver_1_3_reg_offset *tpg_reg = &cam_tpg103_reg; + if (!hw) { + CAM_ERR(CAM_TPG, "invalid params"); + return -EINVAL; + } + + soc_info = hw->soc_info; + + CAM_DBG(CAM_TPG, "TPG[%d] slot(%d,%d) <= dt:%d", + hw->hw_idx, + vc_slot, + dt_slot, + stream->dt); + + val = (((stream->stream_dimension.width & 0xFFFF) << 16) | + (stream->stream_dimension.height & 0xFFFF)); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_dt_0_cfg_0 + + (0x60 * vc_slot) + (dt_slot * 0x0c)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_dt%d_cfg_0=0x%x", + hw->hw_idx, + vc_slot, dt_slot, val); + + cam_io_w_mb(stream->dt, + soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_dt_0_cfg_1 + + (0x60 * vc_slot) + (dt_slot * 0x0c)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_dt%d_cfg_1=0x%x", + hw->hw_idx, + vc_slot, dt_slot, stream->dt); + + val = ((get_tpg_encode_format(stream->pixel_depth) & 0xF) << + tpg_reg->tpg_dt_encode_format_shift) | + get_tpg_payload_mode(stream->pattern_type); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_dt_0_cfg_2 + + (0x60 * vc_slot) + (dt_slot * 0x0c)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_dt%d_cfg_2=0x%x", + hw->hw_idx, + vc_slot, dt_slot, val); + + return 0; +} + +static int configure_vc( + struct tpg_hw *hw, + uint32_t vc_slot, + int num_dts, + struct tpg_stream_config_t *stream) +{ + uint32_t val = 0; + struct cam_hw_soc_info *soc_info = NULL; + struct cam_tpg_ver_1_3_reg_offset *tpg_reg = &cam_tpg103_reg; + if (!hw) { + CAM_ERR(CAM_TPG, "invalid params"); + return -EINVAL; + } + + soc_info = hw->soc_info; + /* Use CFA pattern here */ + if (stream->output_format == TPG_IMAGE_FORMAT_QCFA) + val |= (1 << tpg_reg->tpg_color_bar_qcfa_en_shift); + + if (stream->cb_mode == TPG_COLOR_BAR_MODE_SPLIT) + val |= (1 << tpg_reg->tpg_split_en_shift); + + CAM_DBG(CAM_TPG, "TPG[%d] period: %d", hw->hw_idx, stream->rotate_period); + val |= ((stream->rotate_period & 0x3F) << + tpg_reg->tpg_color_bar_qcfa_rotate_period_shift); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_color_bar_cfg + (0x60 * vc_slot)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_color_bar_cfg=0x%x", + hw->hw_idx, + vc_slot, val); + + val = stream->hbi; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_hbi_cfg + (0x60 * vc_slot)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_hbi_cfg=0x%x", + hw->hw_idx, + vc_slot, val); + + val = stream->vbi; + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_vbi_cfg + (0x60 * vc_slot)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_vbi_cgf=0x%x", + hw->hw_idx, + vc_slot, val); + + cam_io_w_mb(0x12345678, + soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_lfsr_seed + (0x60 * vc_slot)); + + val = ((0 << tpg_reg->tpg_num_frames_shift_val) | + ((num_dts-1) << tpg_reg->tpg_num_dts_shift_val) | + stream->vc); + cam_io_w_mb(val, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_vc0_cfg0 + (0x60 * vc_slot)); + CAM_DBG(CAM_TPG, "TPG[%d] vc%d_cfg0=0x%x", + hw->hw_idx, + vc_slot, val); + + return 0; +} + +static int tpg_hw_v_1_3_reset( + struct tpg_hw *hw, void *data) +{ + struct cam_hw_soc_info *soc_info = NULL; + uint32_t val; + struct cam_tpg_ver_1_3_reg_offset *tpg_reg = &cam_tpg103_reg; + if (!hw) { + CAM_ERR(CAM_TPG, "invalid params"); + return -EINVAL; + } + + soc_info = hw->soc_info; + + /* Clear out tpg_ctrl and irqs before reset */ + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + tpg_reg->tpg_ctrl); + + cam_io_w_mb(0, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_irq_mask); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_irq_clear); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_irq_cmd); + + cam_io_w_mb(1, soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_clear); + + /* Read the version */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + tpg_reg->tpg_hw_version); + CAM_INFO(CAM_TPG, "TPG[%d] TPG HW version: 0x%x started", + hw->hw_idx, val); + return 0; +} + +int tpg_hw_v_1_3_process_cmd( + struct tpg_hw *hw, + uint32_t cmd, + void *arg) +{ + int rc = 0; + if (hw == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + switch(cmd) { + case TPG_CONFIG_VC: + { + struct vc_config_args *vc_config = + (struct vc_config_args *)arg; + + if (vc_config == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + rc = configure_vc(hw, + vc_config->vc_slot, + vc_config->num_dts, + vc_config->stream); + } + break; + case TPG_CONFIG_DT: + { + struct dt_config_args *dt_config = + (struct dt_config_args *)arg; + + if (dt_config == NULL) { + CAM_ERR(CAM_TPG, "invalid argument"); + return -EINVAL; + } + rc = configure_dt(hw, + dt_config->vc_slot, + dt_config->dt_slot, + dt_config->stream); + } + break; + case TPG_CONFIG_CTRL: + { + struct global_config_args *global_args = + (struct global_config_args *)arg; + rc = configure_global_configs(hw, + global_args->num_vcs, + global_args->globalconfig); + } + break; + default: + CAM_ERR(CAM_TPG, "invalid argument"); + break; + } + return rc; +} + +int tpg_hw_v_1_3_start(struct tpg_hw *hw, void *data) +{ + CAM_DBG(CAM_TPG, "TPG V1.3 HWL start"); + return 0; +} + +int tpg_hw_v_1_3_stop(struct tpg_hw *hw, void *data) +{ + CAM_DBG(CAM_TPG, "TPG V1.3 HWL stop"); + tpg_hw_v_1_3_reset(hw, data); + return 0; +} + +int tpg_hw_v_1_3_dump_status(struct tpg_hw *hw, void *data) +{ + struct cam_hw_soc_info *soc_info = NULL; + uint32_t val; + struct cam_tpg_ver_1_3_reg_offset *tpg_reg = &cam_tpg103_reg; + + if (!hw) { + CAM_ERR(CAM_TPG, "invalid params"); + return -EINVAL; + } + + soc_info = hw->soc_info; + CAM_DBG(CAM_TPG, "TPG V1.3 HWL status dump"); + /* Read the version */ + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + tpg_reg->tpg_hw_status); + CAM_INFO(CAM_TPG, "TPG[%d] TPG HW status: 0x%x started", + hw->hw_idx, val); + val = cam_io_r_mb(soc_info->reg_map[0].mem_base + + tpg_reg->tpg_top_irq_status); + CAM_INFO(CAM_TPG, "TPG[%d] TPG HW irq status: 0x%x started", + hw->hw_idx, val); + + return 0; +} + +int tpg_hw_v_1_3_init(struct tpg_hw *hw, void *data) +{ + CAM_DBG(CAM_TPG, "TPG V1.3 HWL init"); + tpg_hw_v_1_3_reset(hw, data); + return 0; +} diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.h b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.h new file mode 100644 index 0000000000..e7ca9982d7 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __TPG_HW_V_1_3_H__ +#define __TPG_HW_V_1_3_H__ + +#include "../tpg_hw.h" + +struct cam_tpg_ver_1_3_reg_offset { + uint32_t tpg_hw_version; + uint32_t tpg_hw_status; + uint32_t tpg_ctrl; + uint32_t tpg_vc0_cfg0; + uint32_t tpg_vc0_lfsr_seed; + uint32_t tpg_vc0_hbi_cfg; + uint32_t tpg_vc0_vbi_cfg; + uint32_t tpg_vc0_color_bar_cfg; + uint32_t tpg_vc0_dt_0_cfg_0; + uint32_t tpg_vc0_dt_0_cfg_1; + uint32_t tpg_vc0_dt_0_cfg_2; + uint32_t tpg_vc0_dt_1_cfg_0; + uint32_t tpg_vc0_dt_1_cfg_1; + uint32_t tpg_vc0_dt_1_cfg_2; + uint32_t tpg_vc0_dt_2_cfg_0; + uint32_t tpg_vc0_dt_2_cfg_1; + uint32_t tpg_vc0_dt_2_cfg_2; + uint32_t tpg_vc0_dt_3_cfg_0; + uint32_t tpg_vc0_dt_3_cfg_1; + uint32_t tpg_vc0_dt_3_cfg_2; + + uint32_t tpg_vc1_cfg0; + uint32_t tpg_vc1_lfsr_seed; + uint32_t tpg_vc1_hbi_cfg; + uint32_t tpg_vc1_vbi_cfg; + uint32_t tpg_vc1_color_bar_cfg; + uint32_t tpg_vc1_dt_0_cfg_0; + uint32_t tpg_vc1_dt_0_cfg_1; + uint32_t tpg_vc1_dt_0_cfg_2; + uint32_t tpg_vc1_dt_1_cfg_0; + uint32_t tpg_vc1_dt_1_cfg_1; + uint32_t tpg_vc1_dt_1_cfg_2; + uint32_t tpg_vc1_dt_2_cfg_0; + uint32_t tpg_vc1_dt_2_cfg_1; + uint32_t tpg_vc1_dt_2_cfg_2; + uint32_t tpg_vc1_dt_3_cfg_0; + uint32_t tpg_vc1_dt_3_cfg_1; + uint32_t tpg_vc1_dt_3_cfg_2; + + uint32_t tpg_vc2_cfg0; + uint32_t tpg_vc2_lfsr_seed; + uint32_t tpg_vc2_hbi_cfg; + uint32_t tpg_vc2_vbi_cfg; + uint32_t tpg_vc2_color_bar_cfg; + uint32_t tpg_vc2_dt_0_cfg_0; + uint32_t tpg_vc2_dt_0_cfg_1; + uint32_t tpg_vc2_dt_0_cfg_2; + uint32_t tpg_vc2_dt_1_cfg_0; + uint32_t tpg_vc2_dt_1_cfg_1; + uint32_t tpg_vc2_dt_1_cfg_2; + uint32_t tpg_vc2_dt_2_cfg_0; + uint32_t tpg_vc2_dt_2_cfg_1; + uint32_t tpg_vc2_dt_2_cfg_2; + uint32_t tpg_vc2_dt_3_cfg_0; + uint32_t tpg_vc2_dt_3_cfg_1; + uint32_t tpg_vc2_dt_3_cfg_2; + + uint32_t tpg_vc3_cfg0; + uint32_t tpg_vc3_lfsr_seed; + uint32_t tpg_vc3_hbi_cfg; + uint32_t tpg_vc3_vbi_cfg; + uint32_t tpg_vc3_color_bar_cfg; + uint32_t tpg_vc3_dt_0_cfg_0; + uint32_t tpg_vc3_dt_0_cfg_1; + uint32_t tpg_vc3_dt_0_cfg_2; + uint32_t tpg_vc3_dt_1_cfg_0; + uint32_t tpg_vc3_dt_1_cfg_1; + uint32_t tpg_vc3_dt_1_cfg_2; + uint32_t tpg_vc3_dt_2_cfg_0; + uint32_t tpg_vc3_dt_2_cfg_1; + uint32_t tpg_vc3_dt_2_cfg_2; + uint32_t tpg_vc3_dt_3_cfg_0; + uint32_t tpg_vc3_dt_3_cfg_1; + uint32_t tpg_vc3_dt_3_cfg_2; + uint32_t tpg_throttle; + uint32_t tpg_top_irq_status; + uint32_t tpg_top_irq_mask; + uint32_t tpg_top_irq_clear; + uint32_t tpg_top_irq_set; + uint32_t tpg_top_irq_cmd; + uint32_t tpg_top_clear; + uint32_t tpg_test_bus_crtl; + uint32_t tpg_spare; + + /* configurations */ + uint32_t major_version; + uint32_t minor_version; + uint32_t version_incr; + uint32_t tpg_en_shift_val; + uint32_t tpg_cphy_dphy_sel_shift_val; + uint32_t tpg_num_active_lanes_shift; + uint32_t tpg_fe_pkt_en_shift; + uint32_t tpg_fs_pkt_en_shift; + uint32_t tpg_line_interleaving_mode_shift; + uint32_t tpg_num_frames_shift_val; + uint32_t tpg_num_dts_shift_val; + uint32_t tpg_v_blank_cnt_shift; + uint32_t tpg_dt_encode_format_shift; + uint32_t tpg_payload_mode_color; + uint32_t tpg_split_en_shift; + uint32_t top_mux_reg_offset; + uint32_t tpg_vc_dt_pattern_id_shift; + uint32_t tpg_num_active_vcs_shift; + uint32_t tpg_color_bar_qcfa_en_shift; + uint32_t tpg_color_bar_qcfa_rotate_period_shift; +}; + + +/** + * @brief initialize the tpg hw instance + * + * @param hw : tpg hw instance + * @param data : argument for initialize + * + * @return : 0 on success + */ +int tpg_hw_v_1_3_init(struct tpg_hw *hw, void *data); + +/** + * @brief start tpg hw + * + * @param hw : tpg hw instance + * @param data : tpg hw instance data + * + * @return : 0 on success + */ +int tpg_hw_v_1_3_start(struct tpg_hw *hw, void *data); + +/** + * @brief stop tpg hw + * + * @param hw : tpg hw instance + * @param data : argument for tpg hw stop + * + * @return : 0 on success + */ +int tpg_hw_v_1_3_stop(struct tpg_hw *hw, void *data); + +/** + * @brief process a command send from hw layer + * + * @param hw : tpg hw instance + * @param cmd : command to process + * @param arg : argument corresponding to command + * + * @return : 0 on success + */ +int tpg_hw_v_1_3_process_cmd(struct tpg_hw *hw, + uint32_t cmd, void *arg); + +/** + * @brief dump hw status registers + * + * @param hw : tpg hw instance + * @param data : argument for status dump + * + * @return : 0 on sucdess + */ +int tpg_hw_v_1_3_dump_status(struct tpg_hw *hw, void *data); + +#endif diff --git a/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h new file mode 100644 index 0000000000..f5aced3853 --- /dev/null +++ b/drivers/cam_sensor_module/cam_tpg/tpg_hw/tpg_hw_v_1_3/tpg_hw_v_1_3_data.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __TPG_HW_V_1_3_DATA_H__ +#define __TPG_HW_V_1_3_DATA_H__ + +#include "../tpg_hw.h" +#include "tpg_hw_v_1_3.h" + +struct tpg_hw_ops tpg_hw_v_1_3_ops = { + .start = tpg_hw_v_1_3_start, + .stop = tpg_hw_v_1_3_stop, + .init = tpg_hw_v_1_3_init, + .process_cmd = tpg_hw_v_1_3_process_cmd, + .dump_status = tpg_hw_v_1_3_dump_status, +}; + +struct tpg_hw_info tpg_v_1_3_hw_info = { + .version = TPG_HW_VERSION_1_3, + .max_vc_channels = 4, + .max_dt_channels_per_vc = 4, + .ops = &tpg_hw_v_1_3_ops, +}; + +#endif diff --git a/drivers/cam_utils/cam_debug_util.c b/drivers/cam_utils/cam_debug_util.c index 169199103b..68eaa3dcf4 100644 --- a/drivers/cam_utils/cam_debug_util.c +++ b/drivers/cam_utils/cam_debug_util.c @@ -227,6 +227,9 @@ const char *cam_get_module_name(unsigned long long module_id) case CAM_PRESIL_CORE: name = "CAM-CORE-PRESIL"; break; + case CAM_TPG: + name = "CAM-TPG"; + break; default: name = "CAM"; break; diff --git a/drivers/cam_utils/cam_debug_util.h b/drivers/cam_utils/cam_debug_util.h index bf359fdf0a..ae6e0c2e54 100644 --- a/drivers/cam_utils/cam_debug_util.h +++ b/drivers/cam_utils/cam_debug_util.h @@ -43,6 +43,7 @@ #define CAM_SFE BIT_ULL(30) #define CAM_CRE BIT_ULL(31) #define CAM_PRESIL_CORE BIT_ULL(32) +#define CAM_TPG BIT_ULL(33) /* Log level types */ #define CAM_TYPE_TRACE (1 << 0) diff --git a/drivers/camera_main.c b/drivers/camera_main.c index 6c92af47a5..4381561662 100644 --- a/drivers/camera_main.c +++ b/drivers/camera_main.c @@ -23,6 +23,7 @@ #include "cam_csiphy_dev.h" #include "cam_eeprom_dev.h" #include "cam_ois_dev.h" +#include "cam_tpg_dev.h" #if IS_REACHABLE(CONFIG_LEDS_QPNP_FLASH_V2) || \ IS_REACHABLE(CONFIG_LEDS_QTI_FLASH) @@ -105,6 +106,7 @@ static const struct camera_submodule_component camera_sensor[] = { {&cam_res_mgr_init, &cam_res_mgr_exit}, {&cam_cci_init_module, &cam_cci_exit_module}, {&cam_csiphy_init_module, &cam_csiphy_exit_module}, + {&cam_tpg_init_module, &cam_tpg_exit_module}, {&cam_actuator_driver_init, &cam_actuator_driver_exit}, {&cam_sensor_driver_init, &cam_sensor_driver_exit}, {&cam_eeprom_driver_init, &cam_eeprom_driver_exit}, diff --git a/drivers/camera_main.h b/drivers/camera_main.h index 7950143ba8..bd46b3aa6c 100644 --- a/drivers/camera_main.h +++ b/drivers/camera_main.h @@ -34,6 +34,7 @@ extern struct platform_driver cam_actuator_platform_driver; extern struct platform_driver cam_sensor_platform_driver; extern struct platform_driver cam_eeprom_platform_driver; extern struct platform_driver cam_ois_platform_driver; +extern struct platform_driver cam_tpg_driver; #if IS_REACHABLE(CONFIG_LEDS_QPNP_FLASH_V2) || \ IS_REACHABLE(CONFIG_LEDS_QTI_FLASH) extern struct platform_driver cam_flash_platform_driver; @@ -104,6 +105,7 @@ static struct platform_driver *const cam_component_drivers[] = { &cam_sensor_platform_driver, &cam_eeprom_platform_driver, &cam_ois_platform_driver, + &cam_tpg_driver, #if IS_REACHABLE(CONFIG_LEDS_QPNP_FLASH_V2) || \ IS_REACHABLE(CONFIG_LEDS_QTI_FLASH) &cam_flash_platform_driver, diff --git a/include/uapi/camera/media/cam_req_mgr.h b/include/uapi/camera/media/cam_req_mgr.h index b96315b2d3..0bc1fe9882 100644 --- a/include/uapi/camera/media/cam_req_mgr.h +++ b/include/uapi/camera/media/cam_req_mgr.h @@ -33,6 +33,7 @@ #define CAM_OPE_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 15) #define CAM_TFE_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 16) #define CAM_CRE_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 17) +#define CAM_TPG_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 18) /* cam_req_mgr hdl info */ #define CAM_REQ_MGR_HDL_IDX_POS 8 diff --git a/include/uapi/camera/media/cam_sensor.h b/include/uapi/camera/media/cam_sensor.h index 2a287b599c..0f7c29d5a0 100644 --- a/include/uapi/camera/media/cam_sensor.h +++ b/include/uapi/camera/media/cam_sensor.h @@ -78,6 +78,13 @@ enum camera_sensor_wait_op_code { CAMERA_SENSOR_WAIT_OP_MAX, }; +enum cam_tpg_packet_opcodes { + CAM_TPG_PACKET_OPCODE_INVALID = 0, + CAM_TPG_PACKET_OPCODE_INITIAL_CONFIG, + CAM_TPG_PACKET_OPCODE_NOP, + CAM_TPG_PACKET_OPCODE_MAX, +}; + enum cam_sensor_packet_opcodes { CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON, CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE, @@ -91,6 +98,78 @@ enum cam_sensor_packet_opcodes { CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP = 127 }; +enum tpg_command_type_t { + TPG_CMD_TYPE_INVALID = 0, + TPG_CMD_TYPE_GLOBAL_CONFIG, + TPG_CMD_TYPE_STREAM_CONFIG, + TPG_CMD_TYPE_ILLUMINATION_CONFIG, + TPG_CMD_TYPE_MAX, +}; + +enum tpg_pattern_t { + TPG_PATTERN_INVALID = 0, + TPG_PATTERN_REAL_IMAGE, + TPG_PATTERN_RANDOM_PIXL, + TPG_PATTERN_RANDOM_INCREMENTING_PIXEL, + TPG_PATTERN_COLOR_BAR, + TPG_PATTERN_ALTERNATING_55_AA, + TPG_PATTERN_ALTERNATING_USER_DEFINED, + TPG_PATTERN_MAX, +}; + +enum tpg_color_bar_mode_t { + TPG_COLOR_BAR_MODE_INVALID = 0, + TPG_COLOR_BAR_MODE_NORMAL, + TPG_COLOR_BAR_MODE_SPLIT, + TPG_COLOR_BAR_MODE_ROTATING, + TPG_COLOR_BAR_MODE_MAX, +}; + +enum tpg_image_format_t { + TPG_IMAGE_FORMAT_INVALID = 0, + TPG_IMAGE_FORMAT_BAYER, + TPG_IMAGE_FORMAT_QCFA, + TPG_IMAGE_FORMAT_YUV, + TPG_IMAGE_FORMAT_JPEG, + TPG_IMAGE_FORMAT_MAX, +}; + +enum tpg_phy_type_t { + TPG_PHY_TYPE_INVALID = 0, + TPG_PHY_TYPE_DPHY, + TPG_PHY_TYPE_CPHY, + TPG_PHY_TYPE_MAX, +}; + +enum tpg_interleaving_format_t { + TPG_INTERLEAVING_FORMAT_INVALID = 0, + TPG_INTERLEAVING_FORMAT_FRAME, + TPG_INTERLEAVING_FORMAT_LINE, + TPG_INTERLEAVING_FORMAT_SHDR, + TPG_INTERLEAVING_FORMAT_SPARSE_PD, + TPG_INTERLEAVING_FORMAT_MAX, +}; + +enum tpg_shutter_t { + TPG_SHUTTER_TYPE_INVALID = 0, + TPG_SHUTTER_TYPE_ROLLING, + TPG_SHUTTER_TYPE_GLOBAL, + TPG_SHUTTER_TYPE_MAX, +}; + +enum tpg_stream_t { + TPG_STREAM_TYPE_INVALID = 0, + TPG_STREAM_TYPE_IMAGE, + TPG_STREAM_TYPE_PDAF, + TPG_STREAM_TYPE_META, + TPG_STREAM_TYPE_MAX, +}; + +enum tpg_cfa_arrangement_t { + TPG_CFA_ARRANGEMENT_TYPE_INVALID = 0, + TPG_CFA_ARRANGEMENT_TYPE_MAX, +}; + /** * struct cam_sensor_query_cap - capabilities info for sensor * @@ -168,6 +247,24 @@ struct cam_ois_query_cap_t { __u16 reserved; } __attribute__((packed)); +/** + * struct cam_tpg_query_cap - capabilities info for tpg + * + * @slot_info : Indicates about the slotId or cell Index + * @version : TPG version , in msb + * @reserved : Reserved for future Use + * @secure_camera : Camera is in secure/Non-secure mode + * @csiphy_slot_id : CSIphy slot id which connected to sensor + */ +struct cam_tpg_query_cap { + __u32 slot_info; + __u32 version; + __u32 secure_camera; + __u32 csiphy_slot_id; + __u32 reserved[2]; +} __attribute__((packed)); + + /** * struct cam_cmd_i2c_info - Contains slave I2C related info * @@ -486,6 +583,23 @@ struct cam_sensor_acquire_dev { __u64 info_handle; } __attribute__((packed)); +/** + * cam_tpg_acquire_dev : Updates tpg acuire cmd + * @device_handle : Updates device handle + * @session_handle : Session handle for acquiring device + * @handle_type : Resource handle type + * @reserved + * @info_handle : Handle to additional info + * needed for sensor sub modules + */ +struct cam_tpg_acquire_dev { + __u32 session_handle; + __u32 device_handle; + __u32 handle_type; + __u32 reserved; + __u64 info_handle; +} __attribute__((packed)); + /** * cam_sensor_streamon_dev : StreamOn command for the sensor * @session_handle : Session handle for acquiring device @@ -503,6 +617,131 @@ struct cam_sensor_streamon_dev { __u64 info_handle; } __attribute__((packed)); + +/** + * stream_dimension : Stream dimension + * + * @left : left pixel locaiton of stream + * @top : top pixel location of stream + * @width : width of the image stream + * @height : Height of the image stream + */ +struct stream_dimension { + uint32_t left; + uint32_t top; + uint32_t width; + uint32_t height; +}; + +/** + * tpg_command_header_t : tpg command common header + * + * @cmd_type : command type + * @size : size of the command including header + * @cmd_version : version of the command associated + */ +struct tpg_command_header_t { + __u32 cmd_type; + ssize_t size; + uint32_t cmd_version; +} __attribute__((packed)); + +/** + * tpg_global_config_t : global configuration command structure + * + * @header : common header + * @phy_type : phy type , cpy , dphy + * @lane_count : number of lanes used + * @interleaving_format : interleaving format used + * @phy_mode : phy mode of operation + * @shutter_type : shutter type + * @mode : if any specific mode needs to configured + * @hbi : horizontal blanking intervel + * @vbi : vertical blanking intervel + * @skip_pattern : frame skip pattern + * @tpg_clock : tpg clock + * @reserved : reserved for future use + */ +struct tpg_global_config_t { + struct tpg_command_header_t header; + enum tpg_phy_type_t phy_type; + uint8_t lane_count; + enum tpg_interleaving_format_t interleaving_format; + uint8_t phy_mode; + enum tpg_shutter_t shutter_type; + uint32_t mode; + uint32_t hbi; + uint32_t vbi; + uint32_t skip_pattern; + uint64_t tpg_clock; + uint32_t reserved[4]; +} __attribute__((packed)); + +/** + * tpg_stream_config_t : stream configuration command + * + * @header: common tpg command header + * @pattern_type : tpg pattern type used in this stream + * @cb_mode : tpg color bar mode used in this stream + * @frame_count : frame count in case of trigger burst mode + * @stream_type : type of stream like image pdaf etc + * @stream_dimension : Dimension of the stream + * @pixel_depth : bits per each pixel + * @cfa_arrangement : color filter arragement + * @output_format : output image format + * @hbi : horizontal blanking intervel + * @vbi : vertical blanking intervel + * @vc : virtual channel of this stream + * @dt : data type of this stream + * @skip_pattern : skip pattern for this stream + * @reserved : reserved for future use + */ +struct tpg_stream_config_t { + struct tpg_command_header_t header; + enum tpg_pattern_t pattern_type; + enum tpg_color_bar_mode_t cb_mode; + uint32_t frame_count; + enum tpg_stream_t stream_type; + struct stream_dimension stream_dimension; + uint8_t pixel_depth; + enum tpg_cfa_arrangement_t cfa_arrangement; + enum tpg_image_format_t output_format; + uint32_t hbi; + uint32_t vbi; + uint16_t vc; + uint16_t dt; + uint32_t skip_pattern; + uint32_t rotate_period; + uint32_t reserved[4]; +} __attribute__((packed)); + +/** + * tpg_illumination_control : illumianation control command + * + * @header : common header for tpg command + * @vc : virtual channel to identify the stream + * @dt : dt to identify the stream + * @exposure_short : short exposure time + * @exposure_mid : mid exposure time + * @exposure_long : long exposure time + * @r_gain : r channel gain + * @g_gain : g channel gain + * @b_gain : b channel gain + * @reserved : reserved for future use + */ +struct tpg_illumination_control { + struct tpg_command_header_t header; + uint16_t vc; + uint16_t dt; + uint32_t exposure_short; + uint32_t exposure_mid; + uint32_t exposure_long; + uint16_t r_gain; + uint16_t g_gain; + uint16_t b_gain; + uint32_t reserved[4]; +} __attribute__((packed)); + /** * struct cam_flash_init : Init command for the flash * @flash_type : flash hw type