From 0fc08356e8bab425a833e656e0cd5b2e121b8647 Mon Sep 17 00:00:00 2001 From: Jian Cao Date: Thu, 4 Apr 2019 22:34:15 +0800 Subject: [PATCH] gdc: add gdc interface with loading fw [1/2] PD#SWPL-4611 Problem: add gdc interface with loading fw Solution: add new gdc interface to request firmware Verify: Verfied on G12B-W400 Change-Id: Id8e0d600eec5f4777511b1fc0e38a1773db9e9cb Signed-off-by: Jian Cao --- drivers/amlogic/media/gdc/app/gdc_module.c | 349 ++++++++++++++++++++++++++++ drivers/amlogic/media/gdc/inc/api/gdc_api.h | 108 ++++++++- 2 files changed, 456 insertions(+), 1 deletion(-) diff --git a/drivers/amlogic/media/gdc/app/gdc_module.c b/drivers/amlogic/media/gdc/app/gdc_module.c index f6f0af3..678980b 100644 --- a/drivers/amlogic/media/gdc/app/gdc_module.c +++ b/drivers/amlogic/media/gdc/app/gdc_module.c @@ -41,6 +41,8 @@ #include #include #include +#include + //gdc configuration sequence #include "gdc_config.h" #include "gdc_dmabuf.h" @@ -794,6 +796,344 @@ void unmap_virt_from_phys(u8 __iomem *vaddr) } } +static void release_config_firmware(struct gdc_settings_with_fw *gs_with_fw) +{ + + if (gs_with_fw->fw_info.virt_addr) + unmap_virt_from_phys(gs_with_fw->fw_info.virt_addr); + if (gs_with_fw->fw_info.cma_pages) { + dma_release_from_contiguous( + &gdc_manager.gdc_dev->pdev->dev, + gs_with_fw->fw_info.cma_pages, + PAGE_ALIGN(gs_with_fw->gdc_config.config_size * 4) + >> PAGE_SHIFT); + + gs_with_fw->fw_info.cma_pages = NULL; + } +} + +static int load_firmware_by_name(struct gdc_settings_with_fw *gs_with_fw) +{ + int ret = -1; + const struct firmware *fw = NULL; + char *path = NULL; + struct fw_info_s *current_fw = &gs_with_fw->fw_info; + struct page *cma_pages = NULL; + void __iomem *virt_addr = NULL; + phys_addr_t phys_addr = 0; + + if (!current_fw->fw_name) { + gdc_log(LOG_ERR, "current firmware name is NULL, invalid\n"); + return -EINVAL; + } + + gdc_log(LOG_DEBUG, "Try to load %s ...\n", current_fw->fw_name); + + path = kzalloc(CONFIG_PATH_LENG, GFP_KERNEL); + if (!path) { + gdc_log(LOG_ERR, "cannot malloc fw_name!\n"); + return -ENOMEM; + } + snprintf(path, (CONFIG_PATH_LENG - 1), "%s/%s", + FIRMWARE_DIR, current_fw->fw_name); + + ret = request_firmware(&fw, path, &gdc_manager.gdc_dev->pdev->dev); + if (ret < 0) { + gdc_log(LOG_ERR, "Error : %d can't load the %s.\n", ret, path); + kfree(path); + return -ENOENT; + } + + if (fw->size <= 0) { + gdc_log(LOG_ERR, + "size error, wrong firmware or no enough mem.\n"); + ret = -ENOMEM; + goto release; + } + + cma_pages = dma_alloc_from_contiguous(&gdc_manager.gdc_dev->pdev->dev, + PAGE_ALIGN(fw->size) >> PAGE_SHIFT, 0); + if (cma_pages != NULL) { + phys_addr = page_to_phys(cma_pages); + virt_addr = map_virt_from_phys(phys_addr, + PAGE_ALIGN(fw->size)); + if (!virt_addr) { + gdc_log(LOG_ERR, "map_virt_from_phys failed\n"); + dma_release_from_contiguous( + &gdc_manager.gdc_dev->pdev->dev, + cma_pages, + PAGE_ALIGN(fw->size) >> PAGE_SHIFT); + ret = -ENOMEM; + goto release; + } + } else { + gdc_log(LOG_ERR, "Failed to alloc dma buff\n"); + ret = -ENOMEM; + goto release; + } + memcpy(virt_addr, (char *)fw->data, fw->size); + + gdc_log(LOG_DEBUG, + "current firmware virt_addr: 0x%p, fw->data: 0x%p.\n", + virt_addr, fw->data); + + gs_with_fw->gdc_config.config_addr = phys_addr; + gs_with_fw->gdc_config.config_size = fw->size / 4; + gs_with_fw->fw_info.cma_pages = cma_pages; + + gdc_log(LOG_DEBUG, "load firmware size : %zd, Name : %s.\n", + fw->size, path); + ret = fw->size; + +release: + release_firmware(fw); + kfree(path); + + return ret; +} + +static long gdc_process_with_fw(struct mgdc_fh_s *fh, + struct gdc_settings_with_fw *gs_with_fw) +{ + long ret = -1; + unsigned long addr = 0; + size_t len; + struct aml_dma_cfg *cfg = NULL; + struct gdc_cmd_s *gdc_cmd = &fh->gdc_cmd; + char *fw_name = NULL; + + if (fh == NULL || gs_with_fw == NULL) { + gdc_log(LOG_ERR, "Error input param\n"); + return -EINVAL; + } + + gs_with_fw->fw_info.virt_addr = NULL; + gs_with_fw->fw_info.cma_pages = NULL; + + memcpy(&(gdc_cmd->gdc_config), &(gs_with_fw->gdc_config), + sizeof(struct gdc_config_s)); + + fw_name = kzalloc(CONFIG_PATH_LENG, GFP_KERNEL); + if (!fw_name) { + gdc_log(LOG_ERR, "cannot malloc fw_name!\n"); + return -ENOMEM; + } + + gdc_cmd->fh = fh; + if (gs_with_fw->output_buffer.mem_alloc_type == AML_GDC_MEM_ION) { + /* ion alloc */ + ret = meson_ion_share_fd_to_phys(fh->ion_client, + gs_with_fw->output_buffer.shared_fd, + (ion_phys_addr_t *)&addr, &len); + if (ret < 0) { + gdc_log(LOG_ERR, "ion import out fd %d failed\n", + gs_with_fw->output_buffer.shared_fd); + ret = -EINVAL; + goto release_fw_name; + } + } else if (gs_with_fw->output_buffer.mem_alloc_type == + AML_GDC_MEM_DMABUF) { + /* dma alloc */ + cfg = &fh->dma_cfg.output_cfg; + cfg->fd = gs_with_fw->output_buffer.y_base_fd; + cfg->dev = &(gdc_manager.gdc_dev->pdev->dev); + cfg->dir = DMA_FROM_DEVICE; + ret = gdc_dma_buffer_get_phys(cfg, &addr); + if (ret < 0) { + gdc_log(LOG_ERR, "dma import out fd %d failed\n", + gs_with_fw->output_buffer.y_base_fd); + ret = -EINVAL; + goto release_fw_name; + } + } + gdc_log(LOG_INFO, "%s, output addr=%lx\n", __func__, addr); + gdc_cmd->base_gdc = 0; + gdc_cmd->buffer_addr = addr; + gdc_cmd->current_addr = gdc_cmd->buffer_addr; + + if (gs_with_fw->input_buffer.mem_alloc_type == AML_GDC_MEM_ION) { + /* ion alloc */ + ret = meson_ion_share_fd_to_phys(fh->ion_client, + gs_with_fw->input_buffer.shared_fd, + (ion_phys_addr_t *)&addr, &len); + if (ret < 0) { + gdc_log(LOG_ERR, "ion import input fd %d failed\n", + gs_with_fw->input_buffer.shared_fd); + ret = -EINVAL; + goto unmap; + } + ret = meson_gdc_set_input_addr(addr, gdc_cmd); + if (ret != 0) { + gdc_log(LOG_ERR, "set input addr failed\n"); + ret = -EINVAL; + goto unmap; + } + } else if (gs_with_fw->input_buffer.mem_alloc_type == + AML_GDC_MEM_DMABUF) { + /* dma alloc */ + gdc_process_input_dma_info(fh, + (struct gdc_settings_ex *)gs_with_fw); + } + gdc_log(LOG_INFO, "%s, input addr=%x\n", + __func__, fh->gdc_cmd.y_base_addr); + + /* load firmware */ + if (gs_with_fw->fw_info.fw_name != NULL) { + ret = load_firmware_by_name(gs_with_fw); + if (ret <= 0) { + gdc_log(LOG_ERR, "line %d,load FW %s failed\n", + __LINE__, gs_with_fw->fw_info.fw_name); + ret = -EINVAL; + goto release_fw; + } + } + + if (ret <= 0 || gs_with_fw->fw_info.fw_name == NULL) { + char in_info[64] = {}; + char out_info[64] = {}; + char *format = NULL; + struct fw_input_info_s *in = &gs_with_fw->fw_info.fw_input_info; + struct fw_output_info_s *out = + &gs_with_fw->fw_info.fw_output_info; + union transform_u *trans = + &gs_with_fw->fw_info.fw_output_info.trans; + + switch (gdc_cmd->gdc_config.format) { + case NV12: + format = "nv12"; + break; + case YV12: + format = "yv12"; + break; + case Y_GREY: + format = "ygrey"; + break; + case YUV444_P: + format = "yuv444p"; + break; + case RGB444_P: + format = "rgb444p"; + break; + default: + gdc_log(LOG_ERR, "unsupported gdc format\n"); + ret = -EINVAL; + goto release_fw; + } + snprintf(in_info, (64 - 1), "%d_%d_%d_%d_%d_%d", + in->with, in->height, + in->fov, in->diameter, + in->offsetX, in->offsetY); + + snprintf(out_info, (64 - 1), "%d_%d_%d_%d-%d_%d_%s", + out->offsetX, out->offsetY, + out->width, out->height, + out->pan, out->tilt, out->zoom); + + switch (gs_with_fw->fw_info.fw_type) { + case EQUISOLID: + snprintf(fw_name, (CONFIG_PATH_LENG - 1), + "equisolid-%s-%s-%s_%s_%d-%s.bin", + in_info, out_info, + trans->fw_equisolid.strengthX, + trans->fw_equisolid.strengthY, + trans->fw_equisolid.rotation, + format); + break; + case CYLINDER: + snprintf(fw_name, (CONFIG_PATH_LENG - 1), + "cylinder-%s-%s-%s_%d-%s.bin", + in_info, out_info, + trans->fw_cylinder.strength, + trans->fw_cylinder.rotation, + format); + break; + case EQUIDISTANT: + snprintf(fw_name, (CONFIG_PATH_LENG - 1), + "equidistant-%s-%s-%s_%d_%d_%d_%d_%d_%d_%d-%s.bin", + in_info, out_info, + trans->fw_equidistant.azimuth, + trans->fw_equidistant.elevation, + trans->fw_equidistant.rotation, + trans->fw_equidistant.fov_width, + trans->fw_equidistant.fov_height, + trans->fw_equidistant.keep_ratio, + trans->fw_equidistant.cylindricityX, + trans->fw_equidistant.cylindricityY, + format); + break; + case CUSTOM: + if (trans->fw_custom.fw_name != NULL) { + snprintf(fw_name, (CONFIG_PATH_LENG - 1), + "custom-%s-%s-%s-%s.bin", + in_info, out_info, + trans->fw_custom.fw_name, + format); + } else { + gdc_log(LOG_ERR, "custom fw_name is NULL\n"); + ret = -EINVAL; + goto release_fw; + } + break; + case AFFINE: + snprintf(fw_name, (CONFIG_PATH_LENG - 1), + "affine-%s-%s-%d-%s.bin", + in_info, out_info, + trans->fw_affine.rotation, + format); + break; + default: + gdc_log(LOG_ERR, "unsupported FW type\n"); + ret = -EINVAL; + goto release_fw; + } + + gs_with_fw->fw_info.fw_name = fw_name; + } + ret = load_firmware_by_name(gs_with_fw); + if (ret <= 0) { + gdc_log(LOG_ERR, "line %d,load FW %s failed\n", + __LINE__, gs_with_fw->fw_info.fw_name); + ret = -EINVAL; + goto release_fw; + } + + gdc_cmd->gdc_config.config_addr = + gs_with_fw->gdc_config.config_addr; + gdc_cmd->gdc_config.config_size = + gs_with_fw->gdc_config.config_size; + + mutex_lock(&fh->gdev->d_mutext); + ret = gdc_run(gdc_cmd); + if (ret < 0) + gdc_log(LOG_ERR, "gdc process failed ret = %ld\n", ret); + + ret = wait_for_completion_timeout(&fh->gdev->d_com, + msecs_to_jiffies(40)); + if (ret == 0) + gdc_log(LOG_ERR, "gdc timeout\n"); + + gdc_stop(gdc_cmd); + mutex_unlock(&fh->gdev->d_mutext); + +release_fw: + release_config_firmware(gs_with_fw); + +unmap: + if (gs_with_fw->input_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) { + gdc_dma_buffer_unmap(&fh->dma_cfg.input_cfg_plane1); + if (gs_with_fw->input_buffer.plane_number == 2) + gdc_dma_buffer_unmap(&fh->dma_cfg.input_cfg_plane2); + } + + if (gs_with_fw->output_buffer.mem_alloc_type == AML_GDC_MEM_DMABUF) + gdc_dma_buffer_unmap(&fh->dma_cfg.output_cfg); + +release_fw_name: + kfree(fw_name); + + return ret; +} +EXPORT_SYMBOL(gdc_process_with_fw); int write_buf_to_file(char *path, char *buf, int size) { int ret = 0; @@ -841,6 +1181,7 @@ static long meson_gdc_ioctl(struct file *file, unsigned int cmd, struct gdc_buf_cfg buf_cfg; struct page *cma_pages = NULL; struct gdc_settings_ex gs_ex; + struct gdc_settings_with_fw gs_with_fw; struct gdc_dmabuf_req_s gdc_req_buf; struct gdc_dmabuf_exp_s gdc_exp_buf; ion_phys_addr_t addr; @@ -1135,6 +1476,14 @@ static long meson_gdc_ioctl(struct file *file, unsigned int cmd, } break; + case GDC_PROCESS_WITH_FW: + ret = copy_from_user(&gs_with_fw, argp, sizeof(gs_with_fw)); + if (ret < 0) + gdc_log(LOG_ERR, "copy from user failed\n"); + memcpy(&gdc_cmd->gdc_config, &gs_with_fw.gdc_config, + sizeof(struct gdc_config_s)); + ret = gdc_process_with_fw(fh, &gs_with_fw); + break; case GDC_PROCESS_EX: ret = copy_from_user(&gs_ex, argp, sizeof(gs_ex)); if (ret < 0) diff --git a/drivers/amlogic/media/gdc/inc/api/gdc_api.h b/drivers/amlogic/media/gdc/inc/api/gdc_api.h index 942bd40..bc46e96 100644 --- a/drivers/amlogic/media/gdc/inc/api/gdc_api.h +++ b/drivers/amlogic/media/gdc/inc/api/gdc_api.h @@ -143,7 +143,8 @@ struct gdc_dmabuf_exp_s { #define GDC_FREE_DMA_BUFF _IOW(GDC_IOC_MAGIC, 0x08, int) #define GDC_SYNC_DEVICE _IOW(GDC_IOC_MAGIC, 0x09, int) #define GDC_SYNC_CPU _IOW(GDC_IOC_MAGIC, 0x0a, int) - +#define GDC_PROCESS_WITH_FW _IOW(GDC_IOC_MAGIC, 0x0b, \ + struct gdc_settings_with_fw) enum { INPUT_BUFF_TYPE = 0x1000, @@ -161,6 +162,15 @@ enum { FMT_MAX }; +enum { + EQUISOLID = 1, + CYLINDER, + EQUIDISTANT, + CUSTOM, + AFFINE, + FW_TYPE_MAX +}; + struct gdc_dma_cfg { int fd; void *dev; @@ -205,6 +215,102 @@ struct gdc_cmd_s { void *fh; }; +/* path: "/vendor/lib/firmware/gdc/" */ +#define FIRMWARE_DIR "gdc" + +struct fw_equisolid_s { + /* float */ + char strengthX[8]; + /* float */ + char strengthY[8]; + int rotation; +}; + +struct fw_cylinder_s { + /* float */ + char strength[8]; + int rotation; +}; + +struct fw_equidistant_s { + /* float */ + char azimuth[8]; + int elevation; + int rotation; + int fov_width; + int fov_height; + bool keep_ratio; + int cylindricityX; + int cylindricityY; +}; + +struct fw_custom_s { + char *fw_name; +}; + + +struct fw_affine_s { + int rotation; +}; + +struct fw_input_info_s { + int with; + int height; + int fov; + int diameter; + int offsetX; + int offsetY; +}; + +union transform_u { + struct fw_equisolid_s fw_equisolid; + struct fw_cylinder_s fw_cylinder; + struct fw_equidistant_s fw_equidistant; + struct fw_custom_s fw_custom; + struct fw_affine_s fw_affine; +}; + +struct fw_output_info_s { + int offsetX; + int offsetY; + int width; + int height; + union transform_u trans; + int pan; + int tilt; + /* float*/ + char zoom[8]; +}; + +struct firmware_info { + unsigned int format; + unsigned int trans_size_type; + char *file_name; + phys_addr_t phys_addr; + void __iomem *virt_addr; + unsigned int size; + struct page *cma_pages; + unsigned int loaded; +}; + +struct fw_info_s { + char *fw_name; + int fw_type; + struct page *cma_pages; + phys_addr_t phys_addr; + void __iomem *virt_addr; + struct fw_input_info_s fw_input_info; + struct fw_output_info_s fw_output_info; +}; + +struct gdc_settings_with_fw { + uint32_t magic; + struct gdc_config_s gdc_config; + struct gdc_buffer_info input_buffer; + struct gdc_buffer_info output_buffer; + struct fw_info_s fw_info; +}; + /** * Configure the output gdc configuration * -- 2.7.4