mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
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 <jian.cao@amlogic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user