gdc: add gdc interface with loading fw [1/2]
authorJian Cao <jian.cao@amlogic.com>
Thu, 4 Apr 2019 14:34:15 +0000 (22:34 +0800)
committerNick Xie <nick@khadas.com>
Mon, 5 Aug 2019 07:11:34 +0000 (15:11 +0800)
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 <jian.cao@amlogic.com>
drivers/amlogic/media/gdc/app/gdc_module.c
drivers/amlogic/media/gdc/inc/api/gdc_api.h

index f6f0af3..678980b 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/semaphore.h>
+#include <linux/firmware.h>
+
 //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)
index 942bd40..bc46e96 100644 (file)
@@ -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
  *